##// END OF EJS Templates
Num ast has been renamed to Constant on 3.8
Matthias Bussonnier -
Show More
@@ -1,958 +1,973 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 def visit_Num(self, node):
613 def visit_Num(self, node):
613 node.n = -node.n
614 node.n = -node.n
614 return node
615 return node
615
616
617 if sys.version_info > (3,8):
618 def visit_Constant(self, node):
619 return self.visit_Num(node)
620
616 class TestAstTransform(unittest.TestCase):
621 class TestAstTransform(unittest.TestCase):
617 def setUp(self):
622 def setUp(self):
618 self.negator = Negator()
623 self.negator = Negator()
619 ip.ast_transformers.append(self.negator)
624 ip.ast_transformers.append(self.negator)
620
625
621 def tearDown(self):
626 def tearDown(self):
622 ip.ast_transformers.remove(self.negator)
627 ip.ast_transformers.remove(self.negator)
623
628
624 def test_run_cell(self):
629 def test_run_cell(self):
625 with tt.AssertPrints('-34'):
630 with tt.AssertPrints('-34'):
626 ip.run_cell('print (12 + 22)')
631 ip.run_cell('print (12 + 22)')
627
632
628 # A named reference to a number shouldn't be transformed.
633 # A named reference to a number shouldn't be transformed.
629 ip.user_ns['n'] = 55
634 ip.user_ns['n'] = 55
630 with tt.AssertNotPrints('-55'):
635 with tt.AssertNotPrints('-55'):
631 ip.run_cell('print (n)')
636 ip.run_cell('print (n)')
632
637
633 def test_timeit(self):
638 def test_timeit(self):
634 called = set()
639 called = set()
635 def f(x):
640 def f(x):
636 called.add(x)
641 called.add(x)
637 ip.push({'f':f})
642 ip.push({'f':f})
638
643
639 with tt.AssertPrints("std. dev. of"):
644 with tt.AssertPrints("std. dev. of"):
640 ip.run_line_magic("timeit", "-n1 f(1)")
645 ip.run_line_magic("timeit", "-n1 f(1)")
641 self.assertEqual(called, {-1})
646 self.assertEqual(called, {-1})
642 called.clear()
647 called.clear()
643
648
644 with tt.AssertPrints("std. dev. of"):
649 with tt.AssertPrints("std. dev. of"):
645 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
650 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
646 self.assertEqual(called, {-2, -3})
651 self.assertEqual(called, {-2, -3})
647
652
648 def test_time(self):
653 def test_time(self):
649 called = []
654 called = []
650 def f(x):
655 def f(x):
651 called.append(x)
656 called.append(x)
652 ip.push({'f':f})
657 ip.push({'f':f})
653
658
654 # Test with an expression
659 # Test with an expression
655 with tt.AssertPrints("Wall time: "):
660 with tt.AssertPrints("Wall time: "):
656 ip.run_line_magic("time", "f(5+9)")
661 ip.run_line_magic("time", "f(5+9)")
657 self.assertEqual(called, [-14])
662 self.assertEqual(called, [-14])
658 called[:] = []
663 called[:] = []
659
664
660 # Test with a statement (different code path)
665 # Test with a statement (different code path)
661 with tt.AssertPrints("Wall time: "):
666 with tt.AssertPrints("Wall time: "):
662 ip.run_line_magic("time", "a = f(-3 + -2)")
667 ip.run_line_magic("time", "a = f(-3 + -2)")
663 self.assertEqual(called, [5])
668 self.assertEqual(called, [5])
664
669
665 def test_macro(self):
670 def test_macro(self):
666 ip.push({'a':10})
671 ip.push({'a':10})
667 # The AST transformation makes this do a+=-1
672 # The AST transformation makes this do a+=-1
668 ip.define_macro("amacro", "a+=1\nprint(a)")
673 ip.define_macro("amacro", "a+=1\nprint(a)")
669
674
670 with tt.AssertPrints("9"):
675 with tt.AssertPrints("9"):
671 ip.run_cell("amacro")
676 ip.run_cell("amacro")
672 with tt.AssertPrints("8"):
677 with tt.AssertPrints("8"):
673 ip.run_cell("amacro")
678 ip.run_cell("amacro")
674
679
675 class IntegerWrapper(ast.NodeTransformer):
680 class IntegerWrapper(ast.NodeTransformer):
676 """Wraps all integers in a call to Integer()"""
681 """Wraps all integers in a call to Integer()"""
677 def visit_Num(self, node):
682 def visit_Num(self, node):
678 if isinstance(node.n, int):
683 if isinstance(node.n, int):
679 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
684 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
680 args=[node], keywords=[])
685 args=[node], keywords=[])
681 return node
686 return node
682
687
688 if sys.version_info > (3,8):
689 def visit_Constant(self, node):
690 return self.visit_Num(node)
691
692
683 class TestAstTransform2(unittest.TestCase):
693 class TestAstTransform2(unittest.TestCase):
684 def setUp(self):
694 def setUp(self):
685 self.intwrapper = IntegerWrapper()
695 self.intwrapper = IntegerWrapper()
686 ip.ast_transformers.append(self.intwrapper)
696 ip.ast_transformers.append(self.intwrapper)
687
697
688 self.calls = []
698 self.calls = []
689 def Integer(*args):
699 def Integer(*args):
690 self.calls.append(args)
700 self.calls.append(args)
691 return args
701 return args
692 ip.push({"Integer": Integer})
702 ip.push({"Integer": Integer})
693
703
694 def tearDown(self):
704 def tearDown(self):
695 ip.ast_transformers.remove(self.intwrapper)
705 ip.ast_transformers.remove(self.intwrapper)
696 del ip.user_ns['Integer']
706 del ip.user_ns['Integer']
697
707
698 def test_run_cell(self):
708 def test_run_cell(self):
699 ip.run_cell("n = 2")
709 ip.run_cell("n = 2")
700 self.assertEqual(self.calls, [(2,)])
710 self.assertEqual(self.calls, [(2,)])
701
711
702 # This shouldn't throw an error
712 # This shouldn't throw an error
703 ip.run_cell("o = 2.0")
713 ip.run_cell("o = 2.0")
704 self.assertEqual(ip.user_ns['o'], 2.0)
714 self.assertEqual(ip.user_ns['o'], 2.0)
705
715
706 def test_timeit(self):
716 def test_timeit(self):
707 called = set()
717 called = set()
708 def f(x):
718 def f(x):
709 called.add(x)
719 called.add(x)
710 ip.push({'f':f})
720 ip.push({'f':f})
711
721
712 with tt.AssertPrints("std. dev. of"):
722 with tt.AssertPrints("std. dev. of"):
713 ip.run_line_magic("timeit", "-n1 f(1)")
723 ip.run_line_magic("timeit", "-n1 f(1)")
714 self.assertEqual(called, {(1,)})
724 self.assertEqual(called, {(1,)})
715 called.clear()
725 called.clear()
716
726
717 with tt.AssertPrints("std. dev. of"):
727 with tt.AssertPrints("std. dev. of"):
718 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
728 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
719 self.assertEqual(called, {(2,), (3,)})
729 self.assertEqual(called, {(2,), (3,)})
720
730
721 class ErrorTransformer(ast.NodeTransformer):
731 class ErrorTransformer(ast.NodeTransformer):
722 """Throws an error when it sees a number."""
732 """Throws an error when it sees a number."""
723 def visit_Num(self, node):
733 def visit_Num(self, node):
724 raise ValueError("test")
734 raise ValueError("test")
725
735
736 if sys.version_info > (3,8):
737 def visit_Constant(self, node):
738 return self.visit_Num(node)
739
740
726 class TestAstTransformError(unittest.TestCase):
741 class TestAstTransformError(unittest.TestCase):
727 def test_unregistering(self):
742 def test_unregistering(self):
728 err_transformer = ErrorTransformer()
743 err_transformer = ErrorTransformer()
729 ip.ast_transformers.append(err_transformer)
744 ip.ast_transformers.append(err_transformer)
730
745
731 with tt.AssertPrints("unregister", channel='stderr'):
746 with tt.AssertPrints("unregister", channel='stderr'):
732 ip.run_cell("1 + 2")
747 ip.run_cell("1 + 2")
733
748
734 # This should have been removed.
749 # This should have been removed.
735 nt.assert_not_in(err_transformer, ip.ast_transformers)
750 nt.assert_not_in(err_transformer, ip.ast_transformers)
736
751
737
752
738 class StringRejector(ast.NodeTransformer):
753 class StringRejector(ast.NodeTransformer):
739 """Throws an InputRejected when it sees a string literal.
754 """Throws an InputRejected when it sees a string literal.
740
755
741 Used to verify that NodeTransformers can signal that a piece of code should
756 Used to verify that NodeTransformers can signal that a piece of code should
742 not be executed by throwing an InputRejected.
757 not be executed by throwing an InputRejected.
743 """
758 """
744
759
745 def visit_Str(self, node):
760 def visit_Str(self, node):
746 raise InputRejected("test")
761 raise InputRejected("test")
747
762
748
763
749 class TestAstTransformInputRejection(unittest.TestCase):
764 class TestAstTransformInputRejection(unittest.TestCase):
750
765
751 def setUp(self):
766 def setUp(self):
752 self.transformer = StringRejector()
767 self.transformer = StringRejector()
753 ip.ast_transformers.append(self.transformer)
768 ip.ast_transformers.append(self.transformer)
754
769
755 def tearDown(self):
770 def tearDown(self):
756 ip.ast_transformers.remove(self.transformer)
771 ip.ast_transformers.remove(self.transformer)
757
772
758 def test_input_rejection(self):
773 def test_input_rejection(self):
759 """Check that NodeTransformers can reject input."""
774 """Check that NodeTransformers can reject input."""
760
775
761 expect_exception_tb = tt.AssertPrints("InputRejected: test")
776 expect_exception_tb = tt.AssertPrints("InputRejected: test")
762 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
777 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
763
778
764 # Run the same check twice to verify that the transformer is not
779 # Run the same check twice to verify that the transformer is not
765 # disabled after raising.
780 # disabled after raising.
766 with expect_exception_tb, expect_no_cell_output:
781 with expect_exception_tb, expect_no_cell_output:
767 ip.run_cell("'unsafe'")
782 ip.run_cell("'unsafe'")
768
783
769 with expect_exception_tb, expect_no_cell_output:
784 with expect_exception_tb, expect_no_cell_output:
770 res = ip.run_cell("'unsafe'")
785 res = ip.run_cell("'unsafe'")
771
786
772 self.assertIsInstance(res.error_before_exec, InputRejected)
787 self.assertIsInstance(res.error_before_exec, InputRejected)
773
788
774 def test__IPYTHON__():
789 def test__IPYTHON__():
775 # This shouldn't raise a NameError, that's all
790 # This shouldn't raise a NameError, that's all
776 __IPYTHON__
791 __IPYTHON__
777
792
778
793
779 class DummyRepr(object):
794 class DummyRepr(object):
780 def __repr__(self):
795 def __repr__(self):
781 return "DummyRepr"
796 return "DummyRepr"
782
797
783 def _repr_html_(self):
798 def _repr_html_(self):
784 return "<b>dummy</b>"
799 return "<b>dummy</b>"
785
800
786 def _repr_javascript_(self):
801 def _repr_javascript_(self):
787 return "console.log('hi');", {'key': 'value'}
802 return "console.log('hi');", {'key': 'value'}
788
803
789
804
790 def test_user_variables():
805 def test_user_variables():
791 # enable all formatters
806 # enable all formatters
792 ip.display_formatter.active_types = ip.display_formatter.format_types
807 ip.display_formatter.active_types = ip.display_formatter.format_types
793
808
794 ip.user_ns['dummy'] = d = DummyRepr()
809 ip.user_ns['dummy'] = d = DummyRepr()
795 keys = {'dummy', 'doesnotexist'}
810 keys = {'dummy', 'doesnotexist'}
796 r = ip.user_expressions({ key:key for key in keys})
811 r = ip.user_expressions({ key:key for key in keys})
797
812
798 nt.assert_equal(keys, set(r.keys()))
813 nt.assert_equal(keys, set(r.keys()))
799 dummy = r['dummy']
814 dummy = r['dummy']
800 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
815 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
801 nt.assert_equal(dummy['status'], 'ok')
816 nt.assert_equal(dummy['status'], 'ok')
802 data = dummy['data']
817 data = dummy['data']
803 metadata = dummy['metadata']
818 metadata = dummy['metadata']
804 nt.assert_equal(data.get('text/html'), d._repr_html_())
819 nt.assert_equal(data.get('text/html'), d._repr_html_())
805 js, jsmd = d._repr_javascript_()
820 js, jsmd = d._repr_javascript_()
806 nt.assert_equal(data.get('application/javascript'), js)
821 nt.assert_equal(data.get('application/javascript'), js)
807 nt.assert_equal(metadata.get('application/javascript'), jsmd)
822 nt.assert_equal(metadata.get('application/javascript'), jsmd)
808
823
809 dne = r['doesnotexist']
824 dne = r['doesnotexist']
810 nt.assert_equal(dne['status'], 'error')
825 nt.assert_equal(dne['status'], 'error')
811 nt.assert_equal(dne['ename'], 'NameError')
826 nt.assert_equal(dne['ename'], 'NameError')
812
827
813 # back to text only
828 # back to text only
814 ip.display_formatter.active_types = ['text/plain']
829 ip.display_formatter.active_types = ['text/plain']
815
830
816 def test_user_expression():
831 def test_user_expression():
817 # enable all formatters
832 # enable all formatters
818 ip.display_formatter.active_types = ip.display_formatter.format_types
833 ip.display_formatter.active_types = ip.display_formatter.format_types
819 query = {
834 query = {
820 'a' : '1 + 2',
835 'a' : '1 + 2',
821 'b' : '1/0',
836 'b' : '1/0',
822 }
837 }
823 r = ip.user_expressions(query)
838 r = ip.user_expressions(query)
824 import pprint
839 import pprint
825 pprint.pprint(r)
840 pprint.pprint(r)
826 nt.assert_equal(set(r.keys()), set(query.keys()))
841 nt.assert_equal(set(r.keys()), set(query.keys()))
827 a = r['a']
842 a = r['a']
828 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
843 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
829 nt.assert_equal(a['status'], 'ok')
844 nt.assert_equal(a['status'], 'ok')
830 data = a['data']
845 data = a['data']
831 metadata = a['metadata']
846 metadata = a['metadata']
832 nt.assert_equal(data.get('text/plain'), '3')
847 nt.assert_equal(data.get('text/plain'), '3')
833
848
834 b = r['b']
849 b = r['b']
835 nt.assert_equal(b['status'], 'error')
850 nt.assert_equal(b['status'], 'error')
836 nt.assert_equal(b['ename'], 'ZeroDivisionError')
851 nt.assert_equal(b['ename'], 'ZeroDivisionError')
837
852
838 # back to text only
853 # back to text only
839 ip.display_formatter.active_types = ['text/plain']
854 ip.display_formatter.active_types = ['text/plain']
840
855
841
856
842
857
843
858
844
859
845 class TestSyntaxErrorTransformer(unittest.TestCase):
860 class TestSyntaxErrorTransformer(unittest.TestCase):
846 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
861 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
847
862
848 @staticmethod
863 @staticmethod
849 def transformer(lines):
864 def transformer(lines):
850 for line in lines:
865 for line in lines:
851 pos = line.find('syntaxerror')
866 pos = line.find('syntaxerror')
852 if pos >= 0:
867 if pos >= 0:
853 e = SyntaxError('input contains "syntaxerror"')
868 e = SyntaxError('input contains "syntaxerror"')
854 e.text = line
869 e.text = line
855 e.offset = pos + 1
870 e.offset = pos + 1
856 raise e
871 raise e
857 return lines
872 return lines
858
873
859 def setUp(self):
874 def setUp(self):
860 ip.input_transformers_post.append(self.transformer)
875 ip.input_transformers_post.append(self.transformer)
861
876
862 def tearDown(self):
877 def tearDown(self):
863 ip.input_transformers_post.remove(self.transformer)
878 ip.input_transformers_post.remove(self.transformer)
864
879
865 def test_syntaxerror_input_transformer(self):
880 def test_syntaxerror_input_transformer(self):
866 with tt.AssertPrints('1234'):
881 with tt.AssertPrints('1234'):
867 ip.run_cell('1234')
882 ip.run_cell('1234')
868 with tt.AssertPrints('SyntaxError: invalid syntax'):
883 with tt.AssertPrints('SyntaxError: invalid syntax'):
869 ip.run_cell('1 2 3') # plain python syntax error
884 ip.run_cell('1 2 3') # plain python syntax error
870 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
885 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
871 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
886 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
872 with tt.AssertPrints('3456'):
887 with tt.AssertPrints('3456'):
873 ip.run_cell('3456')
888 ip.run_cell('3456')
874
889
875
890
876
891
877 def test_warning_suppression():
892 def test_warning_suppression():
878 ip.run_cell("import warnings")
893 ip.run_cell("import warnings")
879 try:
894 try:
880 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
895 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
881 ip.run_cell("warnings.warn('asdf')")
896 ip.run_cell("warnings.warn('asdf')")
882 # Here's the real test -- if we run that again, we should get the
897 # Here's the real test -- if we run that again, we should get the
883 # warning again. Traditionally, each warning was only issued once per
898 # warning again. Traditionally, each warning was only issued once per
884 # IPython session (approximately), even if the user typed in new and
899 # IPython session (approximately), even if the user typed in new and
885 # different code that should have also triggered the warning, leading
900 # different code that should have also triggered the warning, leading
886 # to much confusion.
901 # to much confusion.
887 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
902 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
888 ip.run_cell("warnings.warn('asdf')")
903 ip.run_cell("warnings.warn('asdf')")
889 finally:
904 finally:
890 ip.run_cell("del warnings")
905 ip.run_cell("del warnings")
891
906
892
907
893 def test_deprecation_warning():
908 def test_deprecation_warning():
894 ip.run_cell("""
909 ip.run_cell("""
895 import warnings
910 import warnings
896 def wrn():
911 def wrn():
897 warnings.warn(
912 warnings.warn(
898 "I AM A WARNING",
913 "I AM A WARNING",
899 DeprecationWarning
914 DeprecationWarning
900 )
915 )
901 """)
916 """)
902 try:
917 try:
903 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
918 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
904 ip.run_cell("wrn()")
919 ip.run_cell("wrn()")
905 finally:
920 finally:
906 ip.run_cell("del warnings")
921 ip.run_cell("del warnings")
907 ip.run_cell("del wrn")
922 ip.run_cell("del wrn")
908
923
909
924
910 class TestImportNoDeprecate(tt.TempFileMixin):
925 class TestImportNoDeprecate(tt.TempFileMixin):
911
926
912 def setup(self):
927 def setup(self):
913 """Make a valid python temp file."""
928 """Make a valid python temp file."""
914 self.mktmp("""
929 self.mktmp("""
915 import warnings
930 import warnings
916 def wrn():
931 def wrn():
917 warnings.warn(
932 warnings.warn(
918 "I AM A WARNING",
933 "I AM A WARNING",
919 DeprecationWarning
934 DeprecationWarning
920 )
935 )
921 """)
936 """)
922
937
923 def test_no_dep(self):
938 def test_no_dep(self):
924 """
939 """
925 No deprecation warning should be raised from imported functions
940 No deprecation warning should be raised from imported functions
926 """
941 """
927 ip.run_cell("from {} import wrn".format(self.fname))
942 ip.run_cell("from {} import wrn".format(self.fname))
928
943
929 with tt.AssertNotPrints("I AM A WARNING"):
944 with tt.AssertNotPrints("I AM A WARNING"):
930 ip.run_cell("wrn()")
945 ip.run_cell("wrn()")
931 ip.run_cell("del wrn")
946 ip.run_cell("del wrn")
932
947
933
948
934 def test_custom_exc_count():
949 def test_custom_exc_count():
935 hook = mock.Mock(return_value=None)
950 hook = mock.Mock(return_value=None)
936 ip.set_custom_exc((SyntaxError,), hook)
951 ip.set_custom_exc((SyntaxError,), hook)
937 before = ip.execution_count
952 before = ip.execution_count
938 ip.run_cell("def foo()", store_history=True)
953 ip.run_cell("def foo()", store_history=True)
939 # restore default excepthook
954 # restore default excepthook
940 ip.set_custom_exc((), None)
955 ip.set_custom_exc((), None)
941 nt.assert_equal(hook.call_count, 1)
956 nt.assert_equal(hook.call_count, 1)
942 nt.assert_equal(ip.execution_count, before + 1)
957 nt.assert_equal(ip.execution_count, before + 1)
943
958
944
959
945 def test_run_cell_async():
960 def test_run_cell_async():
946 loop = asyncio.get_event_loop()
961 loop = asyncio.get_event_loop()
947 ip.run_cell("import asyncio")
962 ip.run_cell("import asyncio")
948 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
963 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
949 assert asyncio.iscoroutine(coro)
964 assert asyncio.iscoroutine(coro)
950 result = loop.run_until_complete(coro)
965 result = loop.run_until_complete(coro)
951 assert isinstance(result, interactiveshell.ExecutionResult)
966 assert isinstance(result, interactiveshell.ExecutionResult)
952 assert result.result == 5
967 assert result.result == 5
953
968
954
969
955 def test_should_run_async():
970 def test_should_run_async():
956 assert not ip.should_run_async("a = 5")
971 assert not ip.should_run_async("a = 5")
957 assert ip.should_run_async("await x")
972 assert ip.should_run_async("await x")
958 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
973 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