test_interactiveshell.py
407 lines
| 14.5 KiB
| text/x-python
|
PythonLexer
Jörgen Stenarson
|
r5094 | # -*- coding: utf-8 -*- | ||
Fernando Perez
|
r3300 | """Tests for the key interactiveshell module. | ||
Historically the main classes in interactiveshell have been under-tested. This | ||||
module should grow as many single-method tests as possible to trap many of the | ||||
recurring bugs we seem to encounter with high-level interaction. | ||||
Authors | ||||
------- | ||||
* Fernando Perez | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2011 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
# stdlib | ||||
Jörgen Stenarson
|
r5094 | import os | ||
import shutil | ||||
Fernando Perez
|
r6997 | import sys | ||
Jörgen Stenarson
|
r5094 | import tempfile | ||
Fernando Perez
|
r3300 | import unittest | ||
Jörgen Stenarson
|
r5094 | from os.path import join | ||
MinRK
|
r4794 | from StringIO import StringIO | ||
MinRK
|
r3822 | |||
Fernando Perez
|
r6997 | # third-party | ||
import nose.tools as nt | ||||
# Our own | ||||
Bradley M. Froehle
|
r6643 | from IPython.testing.decorators import skipif | ||
MinRK
|
r3822 | from IPython.utils import io | ||
Fernando Perez
|
r3300 | |||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r6997 | # Globals | ||
#----------------------------------------------------------------------------- | ||||
# This is used by every single test, no point repeating it ad nauseam | ||||
ip = get_ipython() | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r3300 | # Tests | ||
#----------------------------------------------------------------------------- | ||||
class InteractiveShellTestCase(unittest.TestCase): | ||||
def test_naked_string_cells(self): | ||||
"""Test that cells with only naked strings are fully executed""" | ||||
# First, single-line inputs | ||||
ip.run_cell('"a"\n') | ||||
self.assertEquals(ip.user_ns['_'], 'a') | ||||
# And also multi-line cells | ||||
ip.run_cell('"""a\nb"""\n') | ||||
self.assertEquals(ip.user_ns['_'], 'a\nb') | ||||
Paul Ivanov
|
r3499 | |||
Thomas Kluyver
|
r3441 | def test_run_empty_cell(self): | ||
"""Just make sure we don't get a horrible error with a blank | ||||
cell of input. Yes, I did overlook that.""" | ||||
Thomas Kluyver
|
r3706 | old_xc = ip.execution_count | ||
Thomas Kluyver
|
r3441 | ip.run_cell('') | ||
Thomas Kluyver
|
r3706 | self.assertEquals(ip.execution_count, old_xc) | ||
Fernando Perez
|
r3467 | |||
Paul Ivanov
|
r3499 | def test_run_cell_multiline(self): | ||
Fernando Perez
|
r3467 | """Multi-block, multi-line cells must execute correctly. | ||
""" | ||||
src = '\n'.join(["x=1", | ||||
"y=2", | ||||
"if 1:", | ||||
" x += 1", | ||||
" y += 1",]) | ||||
ip.run_cell(src) | ||||
self.assertEquals(ip.user_ns['x'], 2) | ||||
self.assertEquals(ip.user_ns['y'], 3) | ||||
Paul Ivanov
|
r3499 | |||
def test_multiline_string_cells(self): | ||||
Paul Ivanov
|
r3500 | "Code sprinkled with multiline strings should execute (GH-306)" | ||
Paul Ivanov
|
r3499 | ip.run_cell('tmp=0') | ||
self.assertEquals(ip.user_ns['tmp'], 0) | ||||
ip.run_cell('tmp=1;"""a\nb"""\n') | ||||
self.assertEquals(ip.user_ns['tmp'], 1) | ||||
Paul Ivanov
|
r3500 | |||
def test_dont_cache_with_semicolon(self): | ||||
"Ending a line with semicolon should not cache the returned object (GH-307)" | ||||
oldlen = len(ip.user_ns['Out']) | ||||
Thomas Kluyver
|
r4995 | a = ip.run_cell('1;', store_history=True) | ||
Paul Ivanov
|
r3500 | newlen = len(ip.user_ns['Out']) | ||
self.assertEquals(oldlen, newlen) | ||||
#also test the default caching behavior | ||||
Thomas Kluyver
|
r4995 | ip.run_cell('1', store_history=True) | ||
Paul Ivanov
|
r3500 | newlen = len(ip.user_ns['Out']) | ||
self.assertEquals(oldlen+1, newlen) | ||||
Paul Ivanov
|
r3503 | |||
Paul Ivanov
|
r3501 | def test_In_variable(self): | ||
"Verify that In variable grows with user input (GH-284)" | ||||
oldlen = len(ip.user_ns['In']) | ||||
Thomas Kluyver
|
r4995 | ip.run_cell('1;', store_history=True) | ||
Paul Ivanov
|
r3501 | newlen = len(ip.user_ns['In']) | ||
self.assertEquals(oldlen+1, newlen) | ||||
self.assertEquals(ip.user_ns['In'][-1],'1;') | ||||
Thomas Kluyver
|
r3707 | |||
def test_magic_names_in_string(self): | ||||
Thomas Kluyver
|
r3709 | ip.run_cell('a = """\n%exit\n"""') | ||
self.assertEquals(ip.user_ns['a'], '\n%exit\n') | ||||
MinRK
|
r3821 | |||
def test_alias_crash(self): | ||||
"""Errors in prefilter can't crash IPython""" | ||||
ip.run_cell('%alias parts echo first %s second %s') | ||||
MinRK
|
r3822 | # capture stderr: | ||
save_err = io.stderr | ||||
io.stderr = StringIO() | ||||
MinRK
|
r3821 | ip.run_cell('parts 1') | ||
MinRK
|
r3822 | err = io.stderr.getvalue() | ||
io.stderr = save_err | ||||
self.assertEquals(err.split(':')[0], 'ERROR') | ||||
MinRK
|
r3908 | |||
def test_trailing_newline(self): | ||||
"""test that running !(command) does not raise a SyntaxError""" | ||||
ip.run_cell('!(true)\n', False) | ||||
ip.run_cell('!(true)\n\n\n', False) | ||||
Julian Taylor
|
r4281 | |||
def test_gh_597(self): | ||||
Thomas Kluyver
|
r4375 | """Pretty-printing lists of objects with non-ascii reprs may cause | ||
problems.""" | ||||
Julian Taylor
|
r4281 | class Spam(object): | ||
def __repr__(self): | ||||
return "\xe9"*50 | ||||
import IPython.core.formatters | ||||
f = IPython.core.formatters.PlainTextFormatter() | ||||
f([Spam(),Spam()]) | ||||
Thomas Kluyver
|
r4795 | |||
Thomas Kluyver
|
r5456 | |||
Thomas Kluyver
|
r4795 | def test_future_flags(self): | ||
"""Check that future flags are used for parsing code (gh-777)""" | ||||
ip.run_cell('from __future__ import print_function') | ||||
try: | ||||
ip.run_cell('prfunc_return_val = print(1,2, sep=" ")') | ||||
assert 'prfunc_return_val' in ip.user_ns | ||||
finally: | ||||
# Reset compiler flags so we don't mess up other tests. | ||||
ip.compile.reset_compiler_flags() | ||||
Olivier Verdier
|
r4811 | |||
def test_future_unicode(self): | ||||
"""Check that unicode_literals is imported from __future__ (gh #786)""" | ||||
try: | ||||
Olivier Verdier
|
r4820 | ip.run_cell(u'byte_str = "a"') | ||
Olivier Verdier
|
r4821 | assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default | ||
Olivier Verdier
|
r4811 | ip.run_cell('from __future__ import unicode_literals') | ||
Olivier Verdier
|
r4820 | ip.run_cell(u'unicode_str = "a"') | ||
Olivier Verdier
|
r4821 | assert isinstance(ip.user_ns['unicode_str'], unicode) # strings literals are now unicode | ||
Olivier Verdier
|
r4811 | finally: | ||
# Reset compiler flags so we don't mess up other tests. | ||||
ip.compile.reset_compiler_flags() | ||||
Thomas Kluyver
|
r5456 | |||
def test_can_pickle(self): | ||||
"Can we pickle objects defined interactively (GH-29)" | ||||
ip = get_ipython() | ||||
ip.reset() | ||||
ip.run_cell(("class Mylist(list):\n" | ||||
" def __init__(self,x=[]):\n" | ||||
" list.__init__(self,x)")) | ||||
ip.run_cell("w=Mylist([1,2,3])") | ||||
from cPickle import dumps | ||||
# We need to swap in our main module - this is only necessary | ||||
# inside the test framework, because IPython puts the interactive module | ||||
# in place (but the test framework undoes this). | ||||
_main = sys.modules['__main__'] | ||||
sys.modules['__main__'] = ip.user_module | ||||
try: | ||||
res = dumps(ip.user_ns["w"]) | ||||
finally: | ||||
sys.modules['__main__'] = _main | ||||
self.assertTrue(isinstance(res, bytes)) | ||||
def test_global_ns(self): | ||||
"Code in functions must be able to access variables outside them." | ||||
ip = get_ipython() | ||||
ip.run_cell("a = 10") | ||||
Thomas Kluyver
|
r5465 | ip.run_cell(("def f(x):\n" | ||
Thomas Kluyver
|
r5456 | " return x + a")) | ||
ip.run_cell("b = f(12)") | ||||
self.assertEqual(ip.user_ns["b"], 22) | ||||
MinRK
|
r4991 | |||
def test_bad_custom_tb(self): | ||||
"""Check that InteractiveShell is protected from bad custom exception handlers""" | ||||
from IPython.utils import io | ||||
save_stderr = io.stderr | ||||
try: | ||||
# capture stderr | ||||
io.stderr = StringIO() | ||||
MinRK
|
r4999 | ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0) | ||
MinRK
|
r4991 | self.assertEquals(ip.custom_exceptions, (IOError,)) | ||
ip.run_cell(u'raise IOError("foo")') | ||||
self.assertEquals(ip.custom_exceptions, ()) | ||||
self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue()) | ||||
finally: | ||||
io.stderr = save_stderr | ||||
MinRK
|
r4999 | def test_bad_custom_tb_return(self): | ||
"""Check that InteractiveShell is protected from bad return types in custom exception handlers""" | ||||
from IPython.utils import io | ||||
save_stderr = io.stderr | ||||
try: | ||||
# capture stderr | ||||
io.stderr = StringIO() | ||||
ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1) | ||||
self.assertEquals(ip.custom_exceptions, (NameError,)) | ||||
ip.run_cell(u'a=abracadabra') | ||||
self.assertEquals(ip.custom_exceptions, ()) | ||||
self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue()) | ||||
finally: | ||||
io.stderr = save_stderr | ||||
Thomas Kluyver
|
r5068 | def test_drop_by_id(self): | ||
myvars = {"a":object(), "b":object(), "c": object()} | ||||
ip.push(myvars, interactive=False) | ||||
for name in myvars: | ||||
assert name in ip.user_ns, name | ||||
assert name in ip.user_ns_hidden, name | ||||
ip.user_ns['b'] = 12 | ||||
ip.drop_by_id(myvars) | ||||
for name in ["a", "c"]: | ||||
assert name not in ip.user_ns, name | ||||
assert name not in ip.user_ns_hidden, name | ||||
assert ip.user_ns['b'] == 12 | ||||
ip.reset() | ||||
Jörgen Stenarson
|
r5094 | |||
Fernando Perez
|
r5357 | def test_var_expand(self): | ||
Thomas Kluyver
|
r5366 | ip.user_ns['f'] = u'Ca\xf1o' | ||
self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o') | ||||
MinRK
|
r6124 | self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o') | ||
self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1') | ||||
self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2') | ||||
Thomas Kluyver
|
r5366 | |||
ip.user_ns['f'] = b'Ca\xc3\xb1o' | ||||
Fernando Perez
|
r5357 | # This should not raise any exception: | ||
ip.var_expand(u'echo $f') | ||||
MinRK
|
r6124 | |||
Thomas Kluyver
|
r7331 | def test_var_expand_local(self): | ||
Thomas Kluyver
|
r7333 | """Test local variable expansion in !system and %magic calls""" | ||
# !system | ||||
Thomas Kluyver
|
r7331 | ip.run_cell('def test():\n' | ||
' lvar = "ttt"\n' | ||||
' ret = !echo {lvar}\n' | ||||
' return ret[0]\n') | ||||
res = ip.user_ns['test']() | ||||
nt.assert_in('ttt', res) | ||||
Thomas Kluyver
|
r7333 | |||
# %magic | ||||
ip.run_cell('def makemacro():\n' | ||||
' macroname = "macro_var_expand_locals"\n' | ||||
' %macro {macroname} codestr\n') | ||||
ip.user_ns['codestr'] = "str(12)" | ||||
ip.run_cell('makemacro()') | ||||
nt.assert_in('macro_var_expand_locals', ip.user_ns) | ||||
Thomas Kluyver
|
r7331 | |||
MinRK
|
r6124 | def test_bad_var_expand(self): | ||
"""var_expand on invalid formats shouldn't raise""" | ||||
# SyntaxError | ||||
self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}") | ||||
# NameError | ||||
self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}") | ||||
# ZeroDivisionError | ||||
self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}") | ||||
MinRK
|
r6802 | |||
def test_silent_nopostexec(self): | ||||
"""run_cell(silent=True) doesn't invoke post-exec funcs""" | ||||
d = dict(called=False) | ||||
def set_called(): | ||||
d['called'] = True | ||||
ip.register_post_execute(set_called) | ||||
ip.run_cell("1", silent=True) | ||||
self.assertFalse(d['called']) | ||||
# double-check that non-silent exec did what we expected | ||||
# silent to avoid | ||||
ip.run_cell("1") | ||||
self.assertTrue(d['called']) | ||||
# remove post-exec | ||||
ip._post_execute.pop(set_called) | ||||
def test_silent_noadvance(self): | ||||
"""run_cell(silent=True) doesn't advance execution_count""" | ||||
ec = ip.execution_count | ||||
# silent should force store_history=False | ||||
ip.run_cell("1", store_history=True, silent=True) | ||||
self.assertEquals(ec, ip.execution_count) | ||||
# double-check that non-silent exec did what we expected | ||||
# silent to avoid | ||||
ip.run_cell("1", store_history=True) | ||||
self.assertEquals(ec+1, ip.execution_count) | ||||
def test_silent_nodisplayhook(self): | ||||
"""run_cell(silent=True) doesn't trigger displayhook""" | ||||
d = dict(called=False) | ||||
trap = ip.display_trap | ||||
save_hook = trap.hook | ||||
def failing_hook(*args, **kwargs): | ||||
d['called'] = True | ||||
try: | ||||
trap.hook = failing_hook | ||||
ip.run_cell("1", silent=True) | ||||
self.assertFalse(d['called']) | ||||
# double-check that non-silent exec did what we expected | ||||
# silent to avoid | ||||
ip.run_cell("1") | ||||
self.assertTrue(d['called']) | ||||
finally: | ||||
trap.hook = save_hook | ||||
Fernando Perez
|
r5357 | |||
Bradley M. Froehle
|
r6643 | @skipif(sys.version_info[0] >= 3, "softspace removed in py3") | ||
def test_print_softspace(self): | ||||
"""Verify that softspace is handled correctly when executing multiple | ||||
statements. | ||||
In [1]: print 1; print 2 | ||||
1 | ||||
2 | ||||
In [2]: print 1,; print 2 | ||||
1 2 | ||||
""" | ||||
Fernando Perez
|
r6997 | |||
def test_ofind_line_magic(self): | ||||
from IPython.core.magic import register_line_magic | ||||
@register_line_magic | ||||
def lmagic(line): | ||||
"A line magic" | ||||
# Get info on line magic | ||||
lfind = ip._ofind('lmagic') | ||||
info = dict(found=True, isalias=False, ismagic=True, | ||||
namespace = 'IPython internal', obj= lmagic.__wrapped__, | ||||
parent = None) | ||||
nt.assert_equal(lfind, info) | ||||
def test_ofind_cell_magic(self): | ||||
from IPython.core.magic import register_cell_magic | ||||
@register_cell_magic | ||||
def cmagic(line, cell): | ||||
"A cell magic" | ||||
# Get info on cell magic | ||||
find = ip._ofind('cmagic') | ||||
info = dict(found=True, isalias=False, ismagic=True, | ||||
namespace = 'IPython internal', obj= cmagic.__wrapped__, | ||||
parent = None) | ||||
nt.assert_equal(find, info) | ||||
Thomas Kluyver
|
r7110 | |||
def test_custom_exception(self): | ||||
called = [] | ||||
def my_handler(shell, etype, value, tb, tb_offset=None): | ||||
called.append(etype) | ||||
shell.showtraceback((etype, value, tb), tb_offset=tb_offset) | ||||
ip.set_custom_exc((ValueError,), my_handler) | ||||
try: | ||||
ip.run_cell("raise ValueError('test')") | ||||
# Check that this was called, and only once. | ||||
self.assertEqual(called, [ValueError]) | ||||
finally: | ||||
# Reset the custom exception hook | ||||
ip.set_custom_exc((), None) | ||||
Fernando Perez
|
r6997 | |||
Fernando Perez
|
r5357 | |||
Jörgen Stenarson
|
r5094 | class TestSafeExecfileNonAsciiPath(unittest.TestCase): | ||
def setUp(self): | ||||
self.BASETESTDIR = tempfile.mkdtemp() | ||||
self.TESTDIR = join(self.BASETESTDIR, u"åäö") | ||||
os.mkdir(self.TESTDIR) | ||||
with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile: | ||||
sfile.write("pass\n") | ||||
self.oldpath = os.getcwdu() | ||||
os.chdir(self.TESTDIR) | ||||
self.fname = u"åäötestscript.py" | ||||
def tearDown(self): | ||||
os.chdir(self.oldpath) | ||||
shutil.rmtree(self.BASETESTDIR) | ||||
def test_1(self): | ||||
"""Test safe_execfile with non-ascii path | ||||
""" | ||||
Fernando Perez
|
r6997 | ip.safe_execfile(self.fname, {}, raise_exceptions=True) | ||
Jörgen Stenarson
|
r5314 | |||
class TestSystemRaw(unittest.TestCase): | ||||
def test_1(self): | ||||
"""Test system_raw with non-ascii cmd | ||||
""" | ||||
Thomas Kluyver
|
r5318 | cmd = ur'''python -c "'åäö'" ''' | ||
Fernando Perez
|
r6997 | ip.system_raw(cmd) | ||
Fernando Perez
|
r5493 | |||
def test__IPYTHON__(): | ||||
# This shouldn't raise a NameError, that's all | ||||
__IPYTHON__ | ||||