##// END OF EJS Templates
Return current node instead of None
Matthias Bussonnier -
Show More
@@ -1,981 +1,985 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 import asyncio
12 import asyncio
13 import ast
13 import ast
14 import os
14 import os
15 import signal
15 import signal
16 import shutil
16 import shutil
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import unittest
19 import unittest
20 from unittest import mock
20 from unittest import mock
21
21
22 from os.path import join
22 from os.path import join
23
23
24 import nose.tools as nt
24 import nose.tools as nt
25
25
26 from IPython.core.error import InputRejected
26 from IPython.core.error import InputRejected
27 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core.inputtransformer import InputTransformer
28 from IPython.core import interactiveshell
28 from IPython.core import interactiveshell
29 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 )
31 )
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils.process import find_cmd
33 from IPython.utils.process import find_cmd
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Globals
36 # Globals
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # This is used by every single test, no point repeating it ad nauseam
38 # This is used by every single test, no point repeating it ad nauseam
39 ip = get_ipython()
39 ip = get_ipython()
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Tests
42 # Tests
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 class DerivedInterrupt(KeyboardInterrupt):
45 class DerivedInterrupt(KeyboardInterrupt):
46 pass
46 pass
47
47
48 class InteractiveShellTestCase(unittest.TestCase):
48 class InteractiveShellTestCase(unittest.TestCase):
49 def test_naked_string_cells(self):
49 def test_naked_string_cells(self):
50 """Test that cells with only naked strings are fully executed"""
50 """Test that cells with only naked strings are fully executed"""
51 # First, single-line inputs
51 # First, single-line inputs
52 ip.run_cell('"a"\n')
52 ip.run_cell('"a"\n')
53 self.assertEqual(ip.user_ns['_'], 'a')
53 self.assertEqual(ip.user_ns['_'], 'a')
54 # And also multi-line cells
54 # And also multi-line cells
55 ip.run_cell('"""a\nb"""\n')
55 ip.run_cell('"""a\nb"""\n')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
57
57
58 def test_run_empty_cell(self):
58 def test_run_empty_cell(self):
59 """Just make sure we don't get a horrible error with a blank
59 """Just make sure we don't get a horrible error with a blank
60 cell of input. Yes, I did overlook that."""
60 cell of input. Yes, I did overlook that."""
61 old_xc = ip.execution_count
61 old_xc = ip.execution_count
62 res = ip.run_cell('')
62 res = ip.run_cell('')
63 self.assertEqual(ip.execution_count, old_xc)
63 self.assertEqual(ip.execution_count, old_xc)
64 self.assertEqual(res.execution_count, None)
64 self.assertEqual(res.execution_count, None)
65
65
66 def test_run_cell_multiline(self):
66 def test_run_cell_multiline(self):
67 """Multi-block, multi-line cells must execute correctly.
67 """Multi-block, multi-line cells must execute correctly.
68 """
68 """
69 src = '\n'.join(["x=1",
69 src = '\n'.join(["x=1",
70 "y=2",
70 "y=2",
71 "if 1:",
71 "if 1:",
72 " x += 1",
72 " x += 1",
73 " y += 1",])
73 " y += 1",])
74 res = ip.run_cell(src)
74 res = ip.run_cell(src)
75 self.assertEqual(ip.user_ns['x'], 2)
75 self.assertEqual(ip.user_ns['x'], 2)
76 self.assertEqual(ip.user_ns['y'], 3)
76 self.assertEqual(ip.user_ns['y'], 3)
77 self.assertEqual(res.success, True)
77 self.assertEqual(res.success, True)
78 self.assertEqual(res.result, None)
78 self.assertEqual(res.result, None)
79
79
80 def test_multiline_string_cells(self):
80 def test_multiline_string_cells(self):
81 "Code sprinkled with multiline strings should execute (GH-306)"
81 "Code sprinkled with multiline strings should execute (GH-306)"
82 ip.run_cell('tmp=0')
82 ip.run_cell('tmp=0')
83 self.assertEqual(ip.user_ns['tmp'], 0)
83 self.assertEqual(ip.user_ns['tmp'], 0)
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 self.assertEqual(ip.user_ns['tmp'], 1)
85 self.assertEqual(ip.user_ns['tmp'], 1)
86 self.assertEqual(res.success, True)
86 self.assertEqual(res.success, True)
87 self.assertEqual(res.result, "a\nb")
87 self.assertEqual(res.result, "a\nb")
88
88
89 def test_dont_cache_with_semicolon(self):
89 def test_dont_cache_with_semicolon(self):
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 oldlen = len(ip.user_ns['Out'])
91 oldlen = len(ip.user_ns['Out'])
92 for cell in ['1;', '1;1;']:
92 for cell in ['1;', '1;1;']:
93 res = ip.run_cell(cell, store_history=True)
93 res = ip.run_cell(cell, store_history=True)
94 newlen = len(ip.user_ns['Out'])
94 newlen = len(ip.user_ns['Out'])
95 self.assertEqual(oldlen, newlen)
95 self.assertEqual(oldlen, newlen)
96 self.assertIsNone(res.result)
96 self.assertIsNone(res.result)
97 i = 0
97 i = 0
98 #also test the default caching behavior
98 #also test the default caching behavior
99 for cell in ['1', '1;1']:
99 for cell in ['1', '1;1']:
100 ip.run_cell(cell, store_history=True)
100 ip.run_cell(cell, store_history=True)
101 newlen = len(ip.user_ns['Out'])
101 newlen = len(ip.user_ns['Out'])
102 i += 1
102 i += 1
103 self.assertEqual(oldlen+i, newlen)
103 self.assertEqual(oldlen+i, newlen)
104
104
105 def test_syntax_error(self):
105 def test_syntax_error(self):
106 res = ip.run_cell("raise = 3")
106 res = ip.run_cell("raise = 3")
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
108
108
109 def test_In_variable(self):
109 def test_In_variable(self):
110 "Verify that In variable grows with user input (GH-284)"
110 "Verify that In variable grows with user input (GH-284)"
111 oldlen = len(ip.user_ns['In'])
111 oldlen = len(ip.user_ns['In'])
112 ip.run_cell('1;', store_history=True)
112 ip.run_cell('1;', store_history=True)
113 newlen = len(ip.user_ns['In'])
113 newlen = len(ip.user_ns['In'])
114 self.assertEqual(oldlen+1, newlen)
114 self.assertEqual(oldlen+1, newlen)
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
116
116
117 def test_magic_names_in_string(self):
117 def test_magic_names_in_string(self):
118 ip.run_cell('a = """\n%exit\n"""')
118 ip.run_cell('a = """\n%exit\n"""')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120
120
121 def test_trailing_newline(self):
121 def test_trailing_newline(self):
122 """test that running !(command) does not raise a SyntaxError"""
122 """test that running !(command) does not raise a SyntaxError"""
123 ip.run_cell('!(true)\n', False)
123 ip.run_cell('!(true)\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
125
125
126 def test_gh_597(self):
126 def test_gh_597(self):
127 """Pretty-printing lists of objects with non-ascii reprs may cause
127 """Pretty-printing lists of objects with non-ascii reprs may cause
128 problems."""
128 problems."""
129 class Spam(object):
129 class Spam(object):
130 def __repr__(self):
130 def __repr__(self):
131 return "\xe9"*50
131 return "\xe9"*50
132 import IPython.core.formatters
132 import IPython.core.formatters
133 f = IPython.core.formatters.PlainTextFormatter()
133 f = IPython.core.formatters.PlainTextFormatter()
134 f([Spam(),Spam()])
134 f([Spam(),Spam()])
135
135
136
136
137 def test_future_flags(self):
137 def test_future_flags(self):
138 """Check that future flags are used for parsing code (gh-777)"""
138 """Check that future flags are used for parsing code (gh-777)"""
139 ip.run_cell('from __future__ import barry_as_FLUFL')
139 ip.run_cell('from __future__ import barry_as_FLUFL')
140 try:
140 try:
141 ip.run_cell('prfunc_return_val = 1 <> 2')
141 ip.run_cell('prfunc_return_val = 1 <> 2')
142 assert 'prfunc_return_val' in ip.user_ns
142 assert 'prfunc_return_val' in ip.user_ns
143 finally:
143 finally:
144 # Reset compiler flags so we don't mess up other tests.
144 # Reset compiler flags so we don't mess up other tests.
145 ip.compile.reset_compiler_flags()
145 ip.compile.reset_compiler_flags()
146
146
147 def test_can_pickle(self):
147 def test_can_pickle(self):
148 "Can we pickle objects defined interactively (GH-29)"
148 "Can we pickle objects defined interactively (GH-29)"
149 ip = get_ipython()
149 ip = get_ipython()
150 ip.reset()
150 ip.reset()
151 ip.run_cell(("class Mylist(list):\n"
151 ip.run_cell(("class Mylist(list):\n"
152 " def __init__(self,x=[]):\n"
152 " def __init__(self,x=[]):\n"
153 " list.__init__(self,x)"))
153 " list.__init__(self,x)"))
154 ip.run_cell("w=Mylist([1,2,3])")
154 ip.run_cell("w=Mylist([1,2,3])")
155
155
156 from pickle import dumps
156 from pickle import dumps
157
157
158 # We need to swap in our main module - this is only necessary
158 # We need to swap in our main module - this is only necessary
159 # inside the test framework, because IPython puts the interactive module
159 # inside the test framework, because IPython puts the interactive module
160 # in place (but the test framework undoes this).
160 # in place (but the test framework undoes this).
161 _main = sys.modules['__main__']
161 _main = sys.modules['__main__']
162 sys.modules['__main__'] = ip.user_module
162 sys.modules['__main__'] = ip.user_module
163 try:
163 try:
164 res = dumps(ip.user_ns["w"])
164 res = dumps(ip.user_ns["w"])
165 finally:
165 finally:
166 sys.modules['__main__'] = _main
166 sys.modules['__main__'] = _main
167 self.assertTrue(isinstance(res, bytes))
167 self.assertTrue(isinstance(res, bytes))
168
168
169 def test_global_ns(self):
169 def test_global_ns(self):
170 "Code in functions must be able to access variables outside them."
170 "Code in functions must be able to access variables outside them."
171 ip = get_ipython()
171 ip = get_ipython()
172 ip.run_cell("a = 10")
172 ip.run_cell("a = 10")
173 ip.run_cell(("def f(x):\n"
173 ip.run_cell(("def f(x):\n"
174 " return x + a"))
174 " return x + a"))
175 ip.run_cell("b = f(12)")
175 ip.run_cell("b = f(12)")
176 self.assertEqual(ip.user_ns["b"], 22)
176 self.assertEqual(ip.user_ns["b"], 22)
177
177
178 def test_bad_custom_tb(self):
178 def test_bad_custom_tb(self):
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 self.assertEqual(ip.custom_exceptions, (IOError,))
181 self.assertEqual(ip.custom_exceptions, (IOError,))
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 ip.run_cell(u'raise IOError("foo")')
183 ip.run_cell(u'raise IOError("foo")')
184 self.assertEqual(ip.custom_exceptions, ())
184 self.assertEqual(ip.custom_exceptions, ())
185
185
186 def test_bad_custom_tb_return(self):
186 def test_bad_custom_tb_return(self):
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 self.assertEqual(ip.custom_exceptions, (NameError,))
189 self.assertEqual(ip.custom_exceptions, (NameError,))
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 ip.run_cell(u'a=abracadabra')
191 ip.run_cell(u'a=abracadabra')
192 self.assertEqual(ip.custom_exceptions, ())
192 self.assertEqual(ip.custom_exceptions, ())
193
193
194 def test_drop_by_id(self):
194 def test_drop_by_id(self):
195 myvars = {"a":object(), "b":object(), "c": object()}
195 myvars = {"a":object(), "b":object(), "c": object()}
196 ip.push(myvars, interactive=False)
196 ip.push(myvars, interactive=False)
197 for name in myvars:
197 for name in myvars:
198 assert name in ip.user_ns, name
198 assert name in ip.user_ns, name
199 assert name in ip.user_ns_hidden, name
199 assert name in ip.user_ns_hidden, name
200 ip.user_ns['b'] = 12
200 ip.user_ns['b'] = 12
201 ip.drop_by_id(myvars)
201 ip.drop_by_id(myvars)
202 for name in ["a", "c"]:
202 for name in ["a", "c"]:
203 assert name not in ip.user_ns, name
203 assert name not in ip.user_ns, name
204 assert name not in ip.user_ns_hidden, name
204 assert name not in ip.user_ns_hidden, name
205 assert ip.user_ns['b'] == 12
205 assert ip.user_ns['b'] == 12
206 ip.reset()
206 ip.reset()
207
207
208 def test_var_expand(self):
208 def test_var_expand(self):
209 ip.user_ns['f'] = u'Ca\xf1o'
209 ip.user_ns['f'] = u'Ca\xf1o'
210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214
214
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216
216
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 # This should not raise any exception:
218 # This should not raise any exception:
219 ip.var_expand(u'echo $f')
219 ip.var_expand(u'echo $f')
220
220
221 def test_var_expand_local(self):
221 def test_var_expand_local(self):
222 """Test local variable expansion in !system and %magic calls"""
222 """Test local variable expansion in !system and %magic calls"""
223 # !system
223 # !system
224 ip.run_cell('def test():\n'
224 ip.run_cell('def test():\n'
225 ' lvar = "ttt"\n'
225 ' lvar = "ttt"\n'
226 ' ret = !echo {lvar}\n'
226 ' ret = !echo {lvar}\n'
227 ' return ret[0]\n')
227 ' return ret[0]\n')
228 res = ip.user_ns['test']()
228 res = ip.user_ns['test']()
229 nt.assert_in('ttt', res)
229 nt.assert_in('ttt', res)
230
230
231 # %magic
231 # %magic
232 ip.run_cell('def makemacro():\n'
232 ip.run_cell('def makemacro():\n'
233 ' macroname = "macro_var_expand_locals"\n'
233 ' macroname = "macro_var_expand_locals"\n'
234 ' %macro {macroname} codestr\n')
234 ' %macro {macroname} codestr\n')
235 ip.user_ns['codestr'] = "str(12)"
235 ip.user_ns['codestr'] = "str(12)"
236 ip.run_cell('makemacro()')
236 ip.run_cell('makemacro()')
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238
238
239 def test_var_expand_self(self):
239 def test_var_expand_self(self):
240 """Test variable expansion with the name 'self', which was failing.
240 """Test variable expansion with the name 'self', which was failing.
241
241
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 """
243 """
244 ip.run_cell('class cTest:\n'
244 ip.run_cell('class cTest:\n'
245 ' classvar="see me"\n'
245 ' classvar="see me"\n'
246 ' def test(self):\n'
246 ' def test(self):\n'
247 ' res = !echo Variable: {self.classvar}\n'
247 ' res = !echo Variable: {self.classvar}\n'
248 ' return res[0]\n')
248 ' return res[0]\n')
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
250
250
251 def test_bad_var_expand(self):
251 def test_bad_var_expand(self):
252 """var_expand on invalid formats shouldn't raise"""
252 """var_expand on invalid formats shouldn't raise"""
253 # SyntaxError
253 # SyntaxError
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 # NameError
255 # NameError
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 # ZeroDivisionError
257 # ZeroDivisionError
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259
259
260 def test_silent_postexec(self):
260 def test_silent_postexec(self):
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 pre_explicit = mock.Mock()
262 pre_explicit = mock.Mock()
263 pre_always = mock.Mock()
263 pre_always = mock.Mock()
264 post_explicit = mock.Mock()
264 post_explicit = mock.Mock()
265 post_always = mock.Mock()
265 post_always = mock.Mock()
266 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
266 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
267
267
268 ip.events.register('pre_run_cell', pre_explicit)
268 ip.events.register('pre_run_cell', pre_explicit)
269 ip.events.register('pre_execute', pre_always)
269 ip.events.register('pre_execute', pre_always)
270 ip.events.register('post_run_cell', post_explicit)
270 ip.events.register('post_run_cell', post_explicit)
271 ip.events.register('post_execute', post_always)
271 ip.events.register('post_execute', post_always)
272
272
273 try:
273 try:
274 ip.run_cell("1", silent=True)
274 ip.run_cell("1", silent=True)
275 assert pre_always.called
275 assert pre_always.called
276 assert not pre_explicit.called
276 assert not pre_explicit.called
277 assert post_always.called
277 assert post_always.called
278 assert not post_explicit.called
278 assert not post_explicit.called
279 # double-check that non-silent exec did what we expected
279 # double-check that non-silent exec did what we expected
280 # silent to avoid
280 # silent to avoid
281 ip.run_cell("1")
281 ip.run_cell("1")
282 assert pre_explicit.called
282 assert pre_explicit.called
283 assert post_explicit.called
283 assert post_explicit.called
284 info, = pre_explicit.call_args[0]
284 info, = pre_explicit.call_args[0]
285 result, = post_explicit.call_args[0]
285 result, = post_explicit.call_args[0]
286 self.assertEqual(info, result.info)
286 self.assertEqual(info, result.info)
287 # check that post hooks are always called
287 # check that post hooks are always called
288 [m.reset_mock() for m in all_mocks]
288 [m.reset_mock() for m in all_mocks]
289 ip.run_cell("syntax error")
289 ip.run_cell("syntax error")
290 assert pre_always.called
290 assert pre_always.called
291 assert pre_explicit.called
291 assert pre_explicit.called
292 assert post_always.called
292 assert post_always.called
293 assert post_explicit.called
293 assert post_explicit.called
294 info, = pre_explicit.call_args[0]
294 info, = pre_explicit.call_args[0]
295 result, = post_explicit.call_args[0]
295 result, = post_explicit.call_args[0]
296 self.assertEqual(info, result.info)
296 self.assertEqual(info, result.info)
297 finally:
297 finally:
298 # remove post-exec
298 # remove post-exec
299 ip.events.unregister('pre_run_cell', pre_explicit)
299 ip.events.unregister('pre_run_cell', pre_explicit)
300 ip.events.unregister('pre_execute', pre_always)
300 ip.events.unregister('pre_execute', pre_always)
301 ip.events.unregister('post_run_cell', post_explicit)
301 ip.events.unregister('post_run_cell', post_explicit)
302 ip.events.unregister('post_execute', post_always)
302 ip.events.unregister('post_execute', post_always)
303
303
304 def test_silent_noadvance(self):
304 def test_silent_noadvance(self):
305 """run_cell(silent=True) doesn't advance execution_count"""
305 """run_cell(silent=True) doesn't advance execution_count"""
306 ec = ip.execution_count
306 ec = ip.execution_count
307 # silent should force store_history=False
307 # silent should force store_history=False
308 ip.run_cell("1", store_history=True, silent=True)
308 ip.run_cell("1", store_history=True, silent=True)
309
309
310 self.assertEqual(ec, ip.execution_count)
310 self.assertEqual(ec, ip.execution_count)
311 # double-check that non-silent exec did what we expected
311 # double-check that non-silent exec did what we expected
312 # silent to avoid
312 # silent to avoid
313 ip.run_cell("1", store_history=True)
313 ip.run_cell("1", store_history=True)
314 self.assertEqual(ec+1, ip.execution_count)
314 self.assertEqual(ec+1, ip.execution_count)
315
315
316 def test_silent_nodisplayhook(self):
316 def test_silent_nodisplayhook(self):
317 """run_cell(silent=True) doesn't trigger displayhook"""
317 """run_cell(silent=True) doesn't trigger displayhook"""
318 d = dict(called=False)
318 d = dict(called=False)
319
319
320 trap = ip.display_trap
320 trap = ip.display_trap
321 save_hook = trap.hook
321 save_hook = trap.hook
322
322
323 def failing_hook(*args, **kwargs):
323 def failing_hook(*args, **kwargs):
324 d['called'] = True
324 d['called'] = True
325
325
326 try:
326 try:
327 trap.hook = failing_hook
327 trap.hook = failing_hook
328 res = ip.run_cell("1", silent=True)
328 res = ip.run_cell("1", silent=True)
329 self.assertFalse(d['called'])
329 self.assertFalse(d['called'])
330 self.assertIsNone(res.result)
330 self.assertIsNone(res.result)
331 # double-check that non-silent exec did what we expected
331 # double-check that non-silent exec did what we expected
332 # silent to avoid
332 # silent to avoid
333 ip.run_cell("1")
333 ip.run_cell("1")
334 self.assertTrue(d['called'])
334 self.assertTrue(d['called'])
335 finally:
335 finally:
336 trap.hook = save_hook
336 trap.hook = save_hook
337
337
338 def test_ofind_line_magic(self):
338 def test_ofind_line_magic(self):
339 from IPython.core.magic import register_line_magic
339 from IPython.core.magic import register_line_magic
340
340
341 @register_line_magic
341 @register_line_magic
342 def lmagic(line):
342 def lmagic(line):
343 "A line magic"
343 "A line magic"
344
344
345 # Get info on line magic
345 # Get info on line magic
346 lfind = ip._ofind('lmagic')
346 lfind = ip._ofind('lmagic')
347 info = dict(found=True, isalias=False, ismagic=True,
347 info = dict(found=True, isalias=False, ismagic=True,
348 namespace = 'IPython internal', obj= lmagic.__wrapped__,
348 namespace = 'IPython internal', obj= lmagic.__wrapped__,
349 parent = None)
349 parent = None)
350 nt.assert_equal(lfind, info)
350 nt.assert_equal(lfind, info)
351
351
352 def test_ofind_cell_magic(self):
352 def test_ofind_cell_magic(self):
353 from IPython.core.magic import register_cell_magic
353 from IPython.core.magic import register_cell_magic
354
354
355 @register_cell_magic
355 @register_cell_magic
356 def cmagic(line, cell):
356 def cmagic(line, cell):
357 "A cell magic"
357 "A cell magic"
358
358
359 # Get info on cell magic
359 # Get info on cell magic
360 find = ip._ofind('cmagic')
360 find = ip._ofind('cmagic')
361 info = dict(found=True, isalias=False, ismagic=True,
361 info = dict(found=True, isalias=False, ismagic=True,
362 namespace = 'IPython internal', obj= cmagic.__wrapped__,
362 namespace = 'IPython internal', obj= cmagic.__wrapped__,
363 parent = None)
363 parent = None)
364 nt.assert_equal(find, info)
364 nt.assert_equal(find, info)
365
365
366 def test_ofind_property_with_error(self):
366 def test_ofind_property_with_error(self):
367 class A(object):
367 class A(object):
368 @property
368 @property
369 def foo(self):
369 def foo(self):
370 raise NotImplementedError()
370 raise NotImplementedError()
371 a = A()
371 a = A()
372
372
373 found = ip._ofind('a.foo', [('locals', locals())])
373 found = ip._ofind('a.foo', [('locals', locals())])
374 info = dict(found=True, isalias=False, ismagic=False,
374 info = dict(found=True, isalias=False, ismagic=False,
375 namespace='locals', obj=A.foo, parent=a)
375 namespace='locals', obj=A.foo, parent=a)
376 nt.assert_equal(found, info)
376 nt.assert_equal(found, info)
377
377
378 def test_ofind_multiple_attribute_lookups(self):
378 def test_ofind_multiple_attribute_lookups(self):
379 class A(object):
379 class A(object):
380 @property
380 @property
381 def foo(self):
381 def foo(self):
382 raise NotImplementedError()
382 raise NotImplementedError()
383
383
384 a = A()
384 a = A()
385 a.a = A()
385 a.a = A()
386 a.a.a = A()
386 a.a.a = A()
387
387
388 found = ip._ofind('a.a.a.foo', [('locals', locals())])
388 found = ip._ofind('a.a.a.foo', [('locals', locals())])
389 info = dict(found=True, isalias=False, ismagic=False,
389 info = dict(found=True, isalias=False, ismagic=False,
390 namespace='locals', obj=A.foo, parent=a.a.a)
390 namespace='locals', obj=A.foo, parent=a.a.a)
391 nt.assert_equal(found, info)
391 nt.assert_equal(found, info)
392
392
393 def test_ofind_slotted_attributes(self):
393 def test_ofind_slotted_attributes(self):
394 class A(object):
394 class A(object):
395 __slots__ = ['foo']
395 __slots__ = ['foo']
396 def __init__(self):
396 def __init__(self):
397 self.foo = 'bar'
397 self.foo = 'bar'
398
398
399 a = A()
399 a = A()
400 found = ip._ofind('a.foo', [('locals', locals())])
400 found = ip._ofind('a.foo', [('locals', locals())])
401 info = dict(found=True, isalias=False, ismagic=False,
401 info = dict(found=True, isalias=False, ismagic=False,
402 namespace='locals', obj=a.foo, parent=a)
402 namespace='locals', obj=a.foo, parent=a)
403 nt.assert_equal(found, info)
403 nt.assert_equal(found, info)
404
404
405 found = ip._ofind('a.bar', [('locals', locals())])
405 found = ip._ofind('a.bar', [('locals', locals())])
406 info = dict(found=False, isalias=False, ismagic=False,
406 info = dict(found=False, isalias=False, ismagic=False,
407 namespace=None, obj=None, parent=a)
407 namespace=None, obj=None, parent=a)
408 nt.assert_equal(found, info)
408 nt.assert_equal(found, info)
409
409
410 def test_ofind_prefers_property_to_instance_level_attribute(self):
410 def test_ofind_prefers_property_to_instance_level_attribute(self):
411 class A(object):
411 class A(object):
412 @property
412 @property
413 def foo(self):
413 def foo(self):
414 return 'bar'
414 return 'bar'
415 a = A()
415 a = A()
416 a.__dict__['foo'] = 'baz'
416 a.__dict__['foo'] = 'baz'
417 nt.assert_equal(a.foo, 'bar')
417 nt.assert_equal(a.foo, 'bar')
418 found = ip._ofind('a.foo', [('locals', locals())])
418 found = ip._ofind('a.foo', [('locals', locals())])
419 nt.assert_is(found['obj'], A.foo)
419 nt.assert_is(found['obj'], A.foo)
420
420
421 def test_custom_syntaxerror_exception(self):
421 def test_custom_syntaxerror_exception(self):
422 called = []
422 called = []
423 def my_handler(shell, etype, value, tb, tb_offset=None):
423 def my_handler(shell, etype, value, tb, tb_offset=None):
424 called.append(etype)
424 called.append(etype)
425 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
425 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
426
426
427 ip.set_custom_exc((SyntaxError,), my_handler)
427 ip.set_custom_exc((SyntaxError,), my_handler)
428 try:
428 try:
429 ip.run_cell("1f")
429 ip.run_cell("1f")
430 # Check that this was called, and only once.
430 # Check that this was called, and only once.
431 self.assertEqual(called, [SyntaxError])
431 self.assertEqual(called, [SyntaxError])
432 finally:
432 finally:
433 # Reset the custom exception hook
433 # Reset the custom exception hook
434 ip.set_custom_exc((), None)
434 ip.set_custom_exc((), None)
435
435
436 def test_custom_exception(self):
436 def test_custom_exception(self):
437 called = []
437 called = []
438 def my_handler(shell, etype, value, tb, tb_offset=None):
438 def my_handler(shell, etype, value, tb, tb_offset=None):
439 called.append(etype)
439 called.append(etype)
440 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
440 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
441
441
442 ip.set_custom_exc((ValueError,), my_handler)
442 ip.set_custom_exc((ValueError,), my_handler)
443 try:
443 try:
444 res = ip.run_cell("raise ValueError('test')")
444 res = ip.run_cell("raise ValueError('test')")
445 # Check that this was called, and only once.
445 # Check that this was called, and only once.
446 self.assertEqual(called, [ValueError])
446 self.assertEqual(called, [ValueError])
447 # Check that the error is on the result object
447 # Check that the error is on the result object
448 self.assertIsInstance(res.error_in_exec, ValueError)
448 self.assertIsInstance(res.error_in_exec, ValueError)
449 finally:
449 finally:
450 # Reset the custom exception hook
450 # Reset the custom exception hook
451 ip.set_custom_exc((), None)
451 ip.set_custom_exc((), None)
452
452
453 def test_mktempfile(self):
453 def test_mktempfile(self):
454 filename = ip.mktempfile()
454 filename = ip.mktempfile()
455 # Check that we can open the file again on Windows
455 # Check that we can open the file again on Windows
456 with open(filename, 'w') as f:
456 with open(filename, 'w') as f:
457 f.write('abc')
457 f.write('abc')
458
458
459 filename = ip.mktempfile(data='blah')
459 filename = ip.mktempfile(data='blah')
460 with open(filename, 'r') as f:
460 with open(filename, 'r') as f:
461 self.assertEqual(f.read(), 'blah')
461 self.assertEqual(f.read(), 'blah')
462
462
463 def test_new_main_mod(self):
463 def test_new_main_mod(self):
464 # Smoketest to check that this accepts a unicode module name
464 # Smoketest to check that this accepts a unicode module name
465 name = u'jiefmw'
465 name = u'jiefmw'
466 mod = ip.new_main_mod(u'%s.py' % name, name)
466 mod = ip.new_main_mod(u'%s.py' % name, name)
467 self.assertEqual(mod.__name__, name)
467 self.assertEqual(mod.__name__, name)
468
468
469 def test_get_exception_only(self):
469 def test_get_exception_only(self):
470 try:
470 try:
471 raise KeyboardInterrupt
471 raise KeyboardInterrupt
472 except KeyboardInterrupt:
472 except KeyboardInterrupt:
473 msg = ip.get_exception_only()
473 msg = ip.get_exception_only()
474 self.assertEqual(msg, 'KeyboardInterrupt\n')
474 self.assertEqual(msg, 'KeyboardInterrupt\n')
475
475
476 try:
476 try:
477 raise DerivedInterrupt("foo")
477 raise DerivedInterrupt("foo")
478 except KeyboardInterrupt:
478 except KeyboardInterrupt:
479 msg = ip.get_exception_only()
479 msg = ip.get_exception_only()
480 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
480 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
481
481
482 def test_inspect_text(self):
482 def test_inspect_text(self):
483 ip.run_cell('a = 5')
483 ip.run_cell('a = 5')
484 text = ip.object_inspect_text('a')
484 text = ip.object_inspect_text('a')
485 self.assertIsInstance(text, str)
485 self.assertIsInstance(text, str)
486
486
487 def test_last_execution_result(self):
487 def test_last_execution_result(self):
488 """ Check that last execution result gets set correctly (GH-10702) """
488 """ Check that last execution result gets set correctly (GH-10702) """
489 result = ip.run_cell('a = 5; a')
489 result = ip.run_cell('a = 5; a')
490 self.assertTrue(ip.last_execution_succeeded)
490 self.assertTrue(ip.last_execution_succeeded)
491 self.assertEqual(ip.last_execution_result.result, 5)
491 self.assertEqual(ip.last_execution_result.result, 5)
492
492
493 result = ip.run_cell('a = x_invalid_id_x')
493 result = ip.run_cell('a = x_invalid_id_x')
494 self.assertFalse(ip.last_execution_succeeded)
494 self.assertFalse(ip.last_execution_succeeded)
495 self.assertFalse(ip.last_execution_result.success)
495 self.assertFalse(ip.last_execution_result.success)
496 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
496 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
497
497
498 def test_reset_aliasing(self):
498 def test_reset_aliasing(self):
499 """ Check that standard posix aliases work after %reset. """
499 """ Check that standard posix aliases work after %reset. """
500 if os.name != 'posix':
500 if os.name != 'posix':
501 return
501 return
502
502
503 ip.reset()
503 ip.reset()
504 for cmd in ('clear', 'more', 'less', 'man'):
504 for cmd in ('clear', 'more', 'less', 'man'):
505 res = ip.run_cell('%' + cmd)
505 res = ip.run_cell('%' + cmd)
506 self.assertEqual(res.success, True)
506 self.assertEqual(res.success, True)
507
507
508
508
509 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
509 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
510
510
511 @onlyif_unicode_paths
511 @onlyif_unicode_paths
512 def setUp(self):
512 def setUp(self):
513 self.BASETESTDIR = tempfile.mkdtemp()
513 self.BASETESTDIR = tempfile.mkdtemp()
514 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
514 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
515 os.mkdir(self.TESTDIR)
515 os.mkdir(self.TESTDIR)
516 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
516 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
517 sfile.write("pass\n")
517 sfile.write("pass\n")
518 self.oldpath = os.getcwd()
518 self.oldpath = os.getcwd()
519 os.chdir(self.TESTDIR)
519 os.chdir(self.TESTDIR)
520 self.fname = u"Γ₯Àâtestscript.py"
520 self.fname = u"Γ₯Àâtestscript.py"
521
521
522 def tearDown(self):
522 def tearDown(self):
523 os.chdir(self.oldpath)
523 os.chdir(self.oldpath)
524 shutil.rmtree(self.BASETESTDIR)
524 shutil.rmtree(self.BASETESTDIR)
525
525
526 @onlyif_unicode_paths
526 @onlyif_unicode_paths
527 def test_1(self):
527 def test_1(self):
528 """Test safe_execfile with non-ascii path
528 """Test safe_execfile with non-ascii path
529 """
529 """
530 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
530 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
531
531
532 class ExitCodeChecks(tt.TempFileMixin):
532 class ExitCodeChecks(tt.TempFileMixin):
533 def test_exit_code_ok(self):
533 def test_exit_code_ok(self):
534 self.system('exit 0')
534 self.system('exit 0')
535 self.assertEqual(ip.user_ns['_exit_code'], 0)
535 self.assertEqual(ip.user_ns['_exit_code'], 0)
536
536
537 def test_exit_code_error(self):
537 def test_exit_code_error(self):
538 self.system('exit 1')
538 self.system('exit 1')
539 self.assertEqual(ip.user_ns['_exit_code'], 1)
539 self.assertEqual(ip.user_ns['_exit_code'], 1)
540
540
541 @skipif(not hasattr(signal, 'SIGALRM'))
541 @skipif(not hasattr(signal, 'SIGALRM'))
542 def test_exit_code_signal(self):
542 def test_exit_code_signal(self):
543 self.mktmp("import signal, time\n"
543 self.mktmp("import signal, time\n"
544 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
544 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
545 "time.sleep(1)\n")
545 "time.sleep(1)\n")
546 self.system("%s %s" % (sys.executable, self.fname))
546 self.system("%s %s" % (sys.executable, self.fname))
547 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
547 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
548
548
549 @onlyif_cmds_exist("csh")
549 @onlyif_cmds_exist("csh")
550 def test_exit_code_signal_csh(self):
550 def test_exit_code_signal_csh(self):
551 SHELL = os.environ.get('SHELL', None)
551 SHELL = os.environ.get('SHELL', None)
552 os.environ['SHELL'] = find_cmd("csh")
552 os.environ['SHELL'] = find_cmd("csh")
553 try:
553 try:
554 self.test_exit_code_signal()
554 self.test_exit_code_signal()
555 finally:
555 finally:
556 if SHELL is not None:
556 if SHELL is not None:
557 os.environ['SHELL'] = SHELL
557 os.environ['SHELL'] = SHELL
558 else:
558 else:
559 del os.environ['SHELL']
559 del os.environ['SHELL']
560
560
561
561
562 class TestSystemRaw(ExitCodeChecks, unittest.TestCase):
562 class TestSystemRaw(ExitCodeChecks, unittest.TestCase):
563 system = ip.system_raw
563 system = ip.system_raw
564
564
565 @onlyif_unicode_paths
565 @onlyif_unicode_paths
566 def test_1(self):
566 def test_1(self):
567 """Test system_raw with non-ascii cmd
567 """Test system_raw with non-ascii cmd
568 """
568 """
569 cmd = u'''python -c "'Γ₯Àâ'" '''
569 cmd = u'''python -c "'Γ₯Àâ'" '''
570 ip.system_raw(cmd)
570 ip.system_raw(cmd)
571
571
572 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
572 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
573 @mock.patch('os.system', side_effect=KeyboardInterrupt)
573 @mock.patch('os.system', side_effect=KeyboardInterrupt)
574 def test_control_c(self, *mocks):
574 def test_control_c(self, *mocks):
575 try:
575 try:
576 self.system("sleep 1 # wont happen")
576 self.system("sleep 1 # wont happen")
577 except KeyboardInterrupt:
577 except KeyboardInterrupt:
578 self.fail("system call should intercept "
578 self.fail("system call should intercept "
579 "keyboard interrupt from subprocess.call")
579 "keyboard interrupt from subprocess.call")
580 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
580 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
581
581
582 # TODO: Exit codes are currently ignored on Windows.
582 # TODO: Exit codes are currently ignored on Windows.
583 class TestSystemPipedExitCode(ExitCodeChecks, unittest.TestCase):
583 class TestSystemPipedExitCode(ExitCodeChecks, unittest.TestCase):
584 system = ip.system_piped
584 system = ip.system_piped
585
585
586 @skip_win32
586 @skip_win32
587 def test_exit_code_ok(self):
587 def test_exit_code_ok(self):
588 ExitCodeChecks.test_exit_code_ok(self)
588 ExitCodeChecks.test_exit_code_ok(self)
589
589
590 @skip_win32
590 @skip_win32
591 def test_exit_code_error(self):
591 def test_exit_code_error(self):
592 ExitCodeChecks.test_exit_code_error(self)
592 ExitCodeChecks.test_exit_code_error(self)
593
593
594 @skip_win32
594 @skip_win32
595 def test_exit_code_signal(self):
595 def test_exit_code_signal(self):
596 ExitCodeChecks.test_exit_code_signal(self)
596 ExitCodeChecks.test_exit_code_signal(self)
597
597
598 class TestModules(tt.TempFileMixin, unittest.TestCase):
598 class TestModules(tt.TempFileMixin, unittest.TestCase):
599 def test_extraneous_loads(self):
599 def test_extraneous_loads(self):
600 """Test we're not loading modules on startup that we shouldn't.
600 """Test we're not loading modules on startup that we shouldn't.
601 """
601 """
602 self.mktmp("import sys\n"
602 self.mktmp("import sys\n"
603 "print('numpy' in sys.modules)\n"
603 "print('numpy' in sys.modules)\n"
604 "print('ipyparallel' in sys.modules)\n"
604 "print('ipyparallel' in sys.modules)\n"
605 "print('ipykernel' in sys.modules)\n"
605 "print('ipykernel' in sys.modules)\n"
606 )
606 )
607 out = "False\nFalse\nFalse\n"
607 out = "False\nFalse\nFalse\n"
608 tt.ipexec_validate(self.fname, out)
608 tt.ipexec_validate(self.fname, out)
609
609
610 class Negator(ast.NodeTransformer):
610 class Negator(ast.NodeTransformer):
611 """Negates all number literals in an AST."""
611 """Negates all number literals in an AST."""
612
612
613 def visit_Num(self, node):
613 def visit_Num(self, node):
614 node.n = -node.n
614 node.n = -node.n
615 return node
615 return node
616
616
617 if sys.version_info > (3,8):
617 if sys.version_info > (3,8):
618 def visit_Constant(self, node):
618 def visit_Constant(self, node):
619 if isinstance(node.value, int):
619 if isinstance(node.value, int):
620 return self.visit_Num(node)
620 return self.visit_Num(node)
621 return node
621
622
622 class TestAstTransform(unittest.TestCase):
623 class TestAstTransform(unittest.TestCase):
623 def setUp(self):
624 def setUp(self):
624 self.negator = Negator()
625 self.negator = Negator()
625 ip.ast_transformers.append(self.negator)
626 ip.ast_transformers.append(self.negator)
626
627
627 def tearDown(self):
628 def tearDown(self):
628 ip.ast_transformers.remove(self.negator)
629 ip.ast_transformers.remove(self.negator)
629
630
630 def test_run_cell(self):
631 def test_run_cell(self):
631 with tt.AssertPrints('-34'):
632 with tt.AssertPrints('-34'):
632 ip.run_cell('print (12 + 22)')
633 ip.run_cell('print (12 + 22)')
633
634
634 # A named reference to a number shouldn't be transformed.
635 # A named reference to a number shouldn't be transformed.
635 ip.user_ns['n'] = 55
636 ip.user_ns['n'] = 55
636 with tt.AssertNotPrints('-55'):
637 with tt.AssertNotPrints('-55'):
637 ip.run_cell('print (n)')
638 ip.run_cell('print (n)')
638
639
639 def test_timeit(self):
640 def test_timeit(self):
640 called = set()
641 called = set()
641 def f(x):
642 def f(x):
642 called.add(x)
643 called.add(x)
643 ip.push({'f':f})
644 ip.push({'f':f})
644
645
645 with tt.AssertPrints("std. dev. of"):
646 with tt.AssertPrints("std. dev. of"):
646 ip.run_line_magic("timeit", "-n1 f(1)")
647 ip.run_line_magic("timeit", "-n1 f(1)")
647 self.assertEqual(called, {-1})
648 self.assertEqual(called, {-1})
648 called.clear()
649 called.clear()
649
650
650 with tt.AssertPrints("std. dev. of"):
651 with tt.AssertPrints("std. dev. of"):
651 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
652 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
652 self.assertEqual(called, {-2, -3})
653 self.assertEqual(called, {-2, -3})
653
654
654 def test_time(self):
655 def test_time(self):
655 called = []
656 called = []
656 def f(x):
657 def f(x):
657 called.append(x)
658 called.append(x)
658 ip.push({'f':f})
659 ip.push({'f':f})
659
660
660 # Test with an expression
661 # Test with an expression
661 with tt.AssertPrints("Wall time: "):
662 with tt.AssertPrints("Wall time: "):
662 ip.run_line_magic("time", "f(5+9)")
663 ip.run_line_magic("time", "f(5+9)")
663 self.assertEqual(called, [-14])
664 self.assertEqual(called, [-14])
664 called[:] = []
665 called[:] = []
665
666
666 # Test with a statement (different code path)
667 # Test with a statement (different code path)
667 with tt.AssertPrints("Wall time: "):
668 with tt.AssertPrints("Wall time: "):
668 ip.run_line_magic("time", "a = f(-3 + -2)")
669 ip.run_line_magic("time", "a = f(-3 + -2)")
669 self.assertEqual(called, [5])
670 self.assertEqual(called, [5])
670
671
671 def test_macro(self):
672 def test_macro(self):
672 ip.push({'a':10})
673 ip.push({'a':10})
673 # The AST transformation makes this do a+=-1
674 # The AST transformation makes this do a+=-1
674 ip.define_macro("amacro", "a+=1\nprint(a)")
675 ip.define_macro("amacro", "a+=1\nprint(a)")
675
676
676 with tt.AssertPrints("9"):
677 with tt.AssertPrints("9"):
677 ip.run_cell("amacro")
678 ip.run_cell("amacro")
678 with tt.AssertPrints("8"):
679 with tt.AssertPrints("8"):
679 ip.run_cell("amacro")
680 ip.run_cell("amacro")
680
681
681 class IntegerWrapper(ast.NodeTransformer):
682 class IntegerWrapper(ast.NodeTransformer):
682 """Wraps all integers in a call to Integer()"""
683 """Wraps all integers in a call to Integer()"""
683 def visit_Num(self, node):
684 def visit_Num(self, node):
684 if isinstance(node.n, int):
685 if isinstance(node.n, int):
685 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
686 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
686 args=[node], keywords=[])
687 args=[node], keywords=[])
687 return node
688 return node
688
689
689 if sys.version_info > (3,8):
690 if sys.version_info > (3,8):
690 def visit_Constant(self, node):
691 def visit_Constant(self, node):
691 if isinstance(node.value, int):
692 if isinstance(node.value, int):
692 return self.visit_Num(node)
693 return self.visit_Num(node)
694 return node
693
695
694
696
695 class TestAstTransform2(unittest.TestCase):
697 class TestAstTransform2(unittest.TestCase):
696 def setUp(self):
698 def setUp(self):
697 self.intwrapper = IntegerWrapper()
699 self.intwrapper = IntegerWrapper()
698 ip.ast_transformers.append(self.intwrapper)
700 ip.ast_transformers.append(self.intwrapper)
699
701
700 self.calls = []
702 self.calls = []
701 def Integer(*args):
703 def Integer(*args):
702 self.calls.append(args)
704 self.calls.append(args)
703 return args
705 return args
704 ip.push({"Integer": Integer})
706 ip.push({"Integer": Integer})
705
707
706 def tearDown(self):
708 def tearDown(self):
707 ip.ast_transformers.remove(self.intwrapper)
709 ip.ast_transformers.remove(self.intwrapper)
708 del ip.user_ns['Integer']
710 del ip.user_ns['Integer']
709
711
710 def test_run_cell(self):
712 def test_run_cell(self):
711 ip.run_cell("n = 2")
713 ip.run_cell("n = 2")
712 self.assertEqual(self.calls, [(2,)])
714 self.assertEqual(self.calls, [(2,)])
713
715
714 # This shouldn't throw an error
716 # This shouldn't throw an error
715 ip.run_cell("o = 2.0")
717 ip.run_cell("o = 2.0")
716 self.assertEqual(ip.user_ns['o'], 2.0)
718 self.assertEqual(ip.user_ns['o'], 2.0)
717
719
718 def test_timeit(self):
720 def test_timeit(self):
719 called = set()
721 called = set()
720 def f(x):
722 def f(x):
721 called.add(x)
723 called.add(x)
722 ip.push({'f':f})
724 ip.push({'f':f})
723
725
724 with tt.AssertPrints("std. dev. of"):
726 with tt.AssertPrints("std. dev. of"):
725 ip.run_line_magic("timeit", "-n1 f(1)")
727 ip.run_line_magic("timeit", "-n1 f(1)")
726 self.assertEqual(called, {(1,)})
728 self.assertEqual(called, {(1,)})
727 called.clear()
729 called.clear()
728
730
729 with tt.AssertPrints("std. dev. of"):
731 with tt.AssertPrints("std. dev. of"):
730 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
732 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
731 self.assertEqual(called, {(2,), (3,)})
733 self.assertEqual(called, {(2,), (3,)})
732
734
733 class ErrorTransformer(ast.NodeTransformer):
735 class ErrorTransformer(ast.NodeTransformer):
734 """Throws an error when it sees a number."""
736 """Throws an error when it sees a number."""
735 def visit_Num(self, node):
737 def visit_Num(self, node):
736 raise ValueError("test")
738 raise ValueError("test")
737
739
738 if sys.version_info > (3,8):
740 if sys.version_info > (3,8):
739 def visit_Constant(self, node):
741 def visit_Constant(self, node):
740 if isinstance(node.value, int):
742 if isinstance(node.value, int):
741 return self.visit_Num(node)
743 return self.visit_Num(node)
744 return node
742
745
743
746
744 class TestAstTransformError(unittest.TestCase):
747 class TestAstTransformError(unittest.TestCase):
745 def test_unregistering(self):
748 def test_unregistering(self):
746 err_transformer = ErrorTransformer()
749 err_transformer = ErrorTransformer()
747 ip.ast_transformers.append(err_transformer)
750 ip.ast_transformers.append(err_transformer)
748
751
749 with tt.AssertPrints("unregister", channel='stderr'):
752 with tt.AssertPrints("unregister", channel='stderr'):
750 ip.run_cell("1 + 2")
753 ip.run_cell("1 + 2")
751
754
752 # This should have been removed.
755 # This should have been removed.
753 nt.assert_not_in(err_transformer, ip.ast_transformers)
756 nt.assert_not_in(err_transformer, ip.ast_transformers)
754
757
755
758
756 class StringRejector(ast.NodeTransformer):
759 class StringRejector(ast.NodeTransformer):
757 """Throws an InputRejected when it sees a string literal.
760 """Throws an InputRejected when it sees a string literal.
758
761
759 Used to verify that NodeTransformers can signal that a piece of code should
762 Used to verify that NodeTransformers can signal that a piece of code should
760 not be executed by throwing an InputRejected.
763 not be executed by throwing an InputRejected.
761 """
764 """
762
765
763 def visit_Str(self, node):
766 def visit_Str(self, node):
764 raise InputRejected("test")
767 raise InputRejected("test")
765
768
766 # 3.8 only
769 # 3.8 only
767 def visit_Constant(self, node):
770 def visit_Constant(self, node):
768 if isinstance(node.value, str):
771 if isinstance(node.value, str):
769 raise InputRejected("test")
772 raise InputRejected("test")
773 return node
770
774
771
775
772 class TestAstTransformInputRejection(unittest.TestCase):
776 class TestAstTransformInputRejection(unittest.TestCase):
773
777
774 def setUp(self):
778 def setUp(self):
775 self.transformer = StringRejector()
779 self.transformer = StringRejector()
776 ip.ast_transformers.append(self.transformer)
780 ip.ast_transformers.append(self.transformer)
777
781
778 def tearDown(self):
782 def tearDown(self):
779 ip.ast_transformers.remove(self.transformer)
783 ip.ast_transformers.remove(self.transformer)
780
784
781 def test_input_rejection(self):
785 def test_input_rejection(self):
782 """Check that NodeTransformers can reject input."""
786 """Check that NodeTransformers can reject input."""
783
787
784 expect_exception_tb = tt.AssertPrints("InputRejected: test")
788 expect_exception_tb = tt.AssertPrints("InputRejected: test")
785 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
789 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
786
790
787 # Run the same check twice to verify that the transformer is not
791 # Run the same check twice to verify that the transformer is not
788 # disabled after raising.
792 # disabled after raising.
789 with expect_exception_tb, expect_no_cell_output:
793 with expect_exception_tb, expect_no_cell_output:
790 ip.run_cell("'unsafe'")
794 ip.run_cell("'unsafe'")
791
795
792 with expect_exception_tb, expect_no_cell_output:
796 with expect_exception_tb, expect_no_cell_output:
793 res = ip.run_cell("'unsafe'")
797 res = ip.run_cell("'unsafe'")
794
798
795 self.assertIsInstance(res.error_before_exec, InputRejected)
799 self.assertIsInstance(res.error_before_exec, InputRejected)
796
800
797 def test__IPYTHON__():
801 def test__IPYTHON__():
798 # This shouldn't raise a NameError, that's all
802 # This shouldn't raise a NameError, that's all
799 __IPYTHON__
803 __IPYTHON__
800
804
801
805
802 class DummyRepr(object):
806 class DummyRepr(object):
803 def __repr__(self):
807 def __repr__(self):
804 return "DummyRepr"
808 return "DummyRepr"
805
809
806 def _repr_html_(self):
810 def _repr_html_(self):
807 return "<b>dummy</b>"
811 return "<b>dummy</b>"
808
812
809 def _repr_javascript_(self):
813 def _repr_javascript_(self):
810 return "console.log('hi');", {'key': 'value'}
814 return "console.log('hi');", {'key': 'value'}
811
815
812
816
813 def test_user_variables():
817 def test_user_variables():
814 # enable all formatters
818 # enable all formatters
815 ip.display_formatter.active_types = ip.display_formatter.format_types
819 ip.display_formatter.active_types = ip.display_formatter.format_types
816
820
817 ip.user_ns['dummy'] = d = DummyRepr()
821 ip.user_ns['dummy'] = d = DummyRepr()
818 keys = {'dummy', 'doesnotexist'}
822 keys = {'dummy', 'doesnotexist'}
819 r = ip.user_expressions({ key:key for key in keys})
823 r = ip.user_expressions({ key:key for key in keys})
820
824
821 nt.assert_equal(keys, set(r.keys()))
825 nt.assert_equal(keys, set(r.keys()))
822 dummy = r['dummy']
826 dummy = r['dummy']
823 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
827 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
824 nt.assert_equal(dummy['status'], 'ok')
828 nt.assert_equal(dummy['status'], 'ok')
825 data = dummy['data']
829 data = dummy['data']
826 metadata = dummy['metadata']
830 metadata = dummy['metadata']
827 nt.assert_equal(data.get('text/html'), d._repr_html_())
831 nt.assert_equal(data.get('text/html'), d._repr_html_())
828 js, jsmd = d._repr_javascript_()
832 js, jsmd = d._repr_javascript_()
829 nt.assert_equal(data.get('application/javascript'), js)
833 nt.assert_equal(data.get('application/javascript'), js)
830 nt.assert_equal(metadata.get('application/javascript'), jsmd)
834 nt.assert_equal(metadata.get('application/javascript'), jsmd)
831
835
832 dne = r['doesnotexist']
836 dne = r['doesnotexist']
833 nt.assert_equal(dne['status'], 'error')
837 nt.assert_equal(dne['status'], 'error')
834 nt.assert_equal(dne['ename'], 'NameError')
838 nt.assert_equal(dne['ename'], 'NameError')
835
839
836 # back to text only
840 # back to text only
837 ip.display_formatter.active_types = ['text/plain']
841 ip.display_formatter.active_types = ['text/plain']
838
842
839 def test_user_expression():
843 def test_user_expression():
840 # enable all formatters
844 # enable all formatters
841 ip.display_formatter.active_types = ip.display_formatter.format_types
845 ip.display_formatter.active_types = ip.display_formatter.format_types
842 query = {
846 query = {
843 'a' : '1 + 2',
847 'a' : '1 + 2',
844 'b' : '1/0',
848 'b' : '1/0',
845 }
849 }
846 r = ip.user_expressions(query)
850 r = ip.user_expressions(query)
847 import pprint
851 import pprint
848 pprint.pprint(r)
852 pprint.pprint(r)
849 nt.assert_equal(set(r.keys()), set(query.keys()))
853 nt.assert_equal(set(r.keys()), set(query.keys()))
850 a = r['a']
854 a = r['a']
851 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
855 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
852 nt.assert_equal(a['status'], 'ok')
856 nt.assert_equal(a['status'], 'ok')
853 data = a['data']
857 data = a['data']
854 metadata = a['metadata']
858 metadata = a['metadata']
855 nt.assert_equal(data.get('text/plain'), '3')
859 nt.assert_equal(data.get('text/plain'), '3')
856
860
857 b = r['b']
861 b = r['b']
858 nt.assert_equal(b['status'], 'error')
862 nt.assert_equal(b['status'], 'error')
859 nt.assert_equal(b['ename'], 'ZeroDivisionError')
863 nt.assert_equal(b['ename'], 'ZeroDivisionError')
860
864
861 # back to text only
865 # back to text only
862 ip.display_formatter.active_types = ['text/plain']
866 ip.display_formatter.active_types = ['text/plain']
863
867
864
868
865
869
866
870
867
871
868 class TestSyntaxErrorTransformer(unittest.TestCase):
872 class TestSyntaxErrorTransformer(unittest.TestCase):
869 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
873 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
870
874
871 @staticmethod
875 @staticmethod
872 def transformer(lines):
876 def transformer(lines):
873 for line in lines:
877 for line in lines:
874 pos = line.find('syntaxerror')
878 pos = line.find('syntaxerror')
875 if pos >= 0:
879 if pos >= 0:
876 e = SyntaxError('input contains "syntaxerror"')
880 e = SyntaxError('input contains "syntaxerror"')
877 e.text = line
881 e.text = line
878 e.offset = pos + 1
882 e.offset = pos + 1
879 raise e
883 raise e
880 return lines
884 return lines
881
885
882 def setUp(self):
886 def setUp(self):
883 ip.input_transformers_post.append(self.transformer)
887 ip.input_transformers_post.append(self.transformer)
884
888
885 def tearDown(self):
889 def tearDown(self):
886 ip.input_transformers_post.remove(self.transformer)
890 ip.input_transformers_post.remove(self.transformer)
887
891
888 def test_syntaxerror_input_transformer(self):
892 def test_syntaxerror_input_transformer(self):
889 with tt.AssertPrints('1234'):
893 with tt.AssertPrints('1234'):
890 ip.run_cell('1234')
894 ip.run_cell('1234')
891 with tt.AssertPrints('SyntaxError: invalid syntax'):
895 with tt.AssertPrints('SyntaxError: invalid syntax'):
892 ip.run_cell('1 2 3') # plain python syntax error
896 ip.run_cell('1 2 3') # plain python syntax error
893 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
897 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
894 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
898 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
895 with tt.AssertPrints('3456'):
899 with tt.AssertPrints('3456'):
896 ip.run_cell('3456')
900 ip.run_cell('3456')
897
901
898
902
899
903
900 def test_warning_suppression():
904 def test_warning_suppression():
901 ip.run_cell("import warnings")
905 ip.run_cell("import warnings")
902 try:
906 try:
903 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
907 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
904 ip.run_cell("warnings.warn('asdf')")
908 ip.run_cell("warnings.warn('asdf')")
905 # Here's the real test -- if we run that again, we should get the
909 # Here's the real test -- if we run that again, we should get the
906 # warning again. Traditionally, each warning was only issued once per
910 # warning again. Traditionally, each warning was only issued once per
907 # IPython session (approximately), even if the user typed in new and
911 # IPython session (approximately), even if the user typed in new and
908 # different code that should have also triggered the warning, leading
912 # different code that should have also triggered the warning, leading
909 # to much confusion.
913 # to much confusion.
910 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
914 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
911 ip.run_cell("warnings.warn('asdf')")
915 ip.run_cell("warnings.warn('asdf')")
912 finally:
916 finally:
913 ip.run_cell("del warnings")
917 ip.run_cell("del warnings")
914
918
915
919
916 def test_deprecation_warning():
920 def test_deprecation_warning():
917 ip.run_cell("""
921 ip.run_cell("""
918 import warnings
922 import warnings
919 def wrn():
923 def wrn():
920 warnings.warn(
924 warnings.warn(
921 "I AM A WARNING",
925 "I AM A WARNING",
922 DeprecationWarning
926 DeprecationWarning
923 )
927 )
924 """)
928 """)
925 try:
929 try:
926 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
930 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
927 ip.run_cell("wrn()")
931 ip.run_cell("wrn()")
928 finally:
932 finally:
929 ip.run_cell("del warnings")
933 ip.run_cell("del warnings")
930 ip.run_cell("del wrn")
934 ip.run_cell("del wrn")
931
935
932
936
933 class TestImportNoDeprecate(tt.TempFileMixin):
937 class TestImportNoDeprecate(tt.TempFileMixin):
934
938
935 def setup(self):
939 def setup(self):
936 """Make a valid python temp file."""
940 """Make a valid python temp file."""
937 self.mktmp("""
941 self.mktmp("""
938 import warnings
942 import warnings
939 def wrn():
943 def wrn():
940 warnings.warn(
944 warnings.warn(
941 "I AM A WARNING",
945 "I AM A WARNING",
942 DeprecationWarning
946 DeprecationWarning
943 )
947 )
944 """)
948 """)
945
949
946 def test_no_dep(self):
950 def test_no_dep(self):
947 """
951 """
948 No deprecation warning should be raised from imported functions
952 No deprecation warning should be raised from imported functions
949 """
953 """
950 ip.run_cell("from {} import wrn".format(self.fname))
954 ip.run_cell("from {} import wrn".format(self.fname))
951
955
952 with tt.AssertNotPrints("I AM A WARNING"):
956 with tt.AssertNotPrints("I AM A WARNING"):
953 ip.run_cell("wrn()")
957 ip.run_cell("wrn()")
954 ip.run_cell("del wrn")
958 ip.run_cell("del wrn")
955
959
956
960
957 def test_custom_exc_count():
961 def test_custom_exc_count():
958 hook = mock.Mock(return_value=None)
962 hook = mock.Mock(return_value=None)
959 ip.set_custom_exc((SyntaxError,), hook)
963 ip.set_custom_exc((SyntaxError,), hook)
960 before = ip.execution_count
964 before = ip.execution_count
961 ip.run_cell("def foo()", store_history=True)
965 ip.run_cell("def foo()", store_history=True)
962 # restore default excepthook
966 # restore default excepthook
963 ip.set_custom_exc((), None)
967 ip.set_custom_exc((), None)
964 nt.assert_equal(hook.call_count, 1)
968 nt.assert_equal(hook.call_count, 1)
965 nt.assert_equal(ip.execution_count, before + 1)
969 nt.assert_equal(ip.execution_count, before + 1)
966
970
967
971
968 def test_run_cell_async():
972 def test_run_cell_async():
969 loop = asyncio.get_event_loop()
973 loop = asyncio.get_event_loop()
970 ip.run_cell("import asyncio")
974 ip.run_cell("import asyncio")
971 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
975 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
972 assert asyncio.iscoroutine(coro)
976 assert asyncio.iscoroutine(coro)
973 result = loop.run_until_complete(coro)
977 result = loop.run_until_complete(coro)
974 assert isinstance(result, interactiveshell.ExecutionResult)
978 assert isinstance(result, interactiveshell.ExecutionResult)
975 assert result.result == 5
979 assert result.result == 5
976
980
977
981
978 def test_should_run_async():
982 def test_should_run_async():
979 assert not ip.should_run_async("a = 5")
983 assert not ip.should_run_async("a = 5")
980 assert ip.should_run_async("await x")
984 assert ip.should_run_async("await x")
981 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
985 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
General Comments 0
You need to be logged in to leave comments. Login now