##// END OF EJS Templates
comment why the changes and for which python version
Matthias Bussonnier -
Show More
@@ -1,985 +1,993 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 # for python 3.7 and earlier
613 def visit_Num(self, node):
614 def visit_Num(self, node):
614 node.n = -node.n
615 node.n = -node.n
615 return node
616 return node
616
617
617 if sys.version_info > (3,8):
618 # for python 3.8+
618 def visit_Constant(self, node):
619 def visit_Constant(self, node):
619 if isinstance(node.value, int):
620 if isinstance(node.value, int):
620 return self.visit_Num(node)
621 return self.visit_Num(node)
621 return node
622 return node
622
623
623 class TestAstTransform(unittest.TestCase):
624 class TestAstTransform(unittest.TestCase):
624 def setUp(self):
625 def setUp(self):
625 self.negator = Negator()
626 self.negator = Negator()
626 ip.ast_transformers.append(self.negator)
627 ip.ast_transformers.append(self.negator)
627
628
628 def tearDown(self):
629 def tearDown(self):
629 ip.ast_transformers.remove(self.negator)
630 ip.ast_transformers.remove(self.negator)
630
631
631 def test_run_cell(self):
632 def test_run_cell(self):
632 with tt.AssertPrints('-34'):
633 with tt.AssertPrints('-34'):
633 ip.run_cell('print (12 + 22)')
634 ip.run_cell('print (12 + 22)')
634
635
635 # A named reference to a number shouldn't be transformed.
636 # A named reference to a number shouldn't be transformed.
636 ip.user_ns['n'] = 55
637 ip.user_ns['n'] = 55
637 with tt.AssertNotPrints('-55'):
638 with tt.AssertNotPrints('-55'):
638 ip.run_cell('print (n)')
639 ip.run_cell('print (n)')
639
640
640 def test_timeit(self):
641 def test_timeit(self):
641 called = set()
642 called = set()
642 def f(x):
643 def f(x):
643 called.add(x)
644 called.add(x)
644 ip.push({'f':f})
645 ip.push({'f':f})
645
646
646 with tt.AssertPrints("std. dev. of"):
647 with tt.AssertPrints("std. dev. of"):
647 ip.run_line_magic("timeit", "-n1 f(1)")
648 ip.run_line_magic("timeit", "-n1 f(1)")
648 self.assertEqual(called, {-1})
649 self.assertEqual(called, {-1})
649 called.clear()
650 called.clear()
650
651
651 with tt.AssertPrints("std. dev. of"):
652 with tt.AssertPrints("std. dev. of"):
652 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
653 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
653 self.assertEqual(called, {-2, -3})
654 self.assertEqual(called, {-2, -3})
654
655
655 def test_time(self):
656 def test_time(self):
656 called = []
657 called = []
657 def f(x):
658 def f(x):
658 called.append(x)
659 called.append(x)
659 ip.push({'f':f})
660 ip.push({'f':f})
660
661
661 # Test with an expression
662 # Test with an expression
662 with tt.AssertPrints("Wall time: "):
663 with tt.AssertPrints("Wall time: "):
663 ip.run_line_magic("time", "f(5+9)")
664 ip.run_line_magic("time", "f(5+9)")
664 self.assertEqual(called, [-14])
665 self.assertEqual(called, [-14])
665 called[:] = []
666 called[:] = []
666
667
667 # Test with a statement (different code path)
668 # Test with a statement (different code path)
668 with tt.AssertPrints("Wall time: "):
669 with tt.AssertPrints("Wall time: "):
669 ip.run_line_magic("time", "a = f(-3 + -2)")
670 ip.run_line_magic("time", "a = f(-3 + -2)")
670 self.assertEqual(called, [5])
671 self.assertEqual(called, [5])
671
672
672 def test_macro(self):
673 def test_macro(self):
673 ip.push({'a':10})
674 ip.push({'a':10})
674 # The AST transformation makes this do a+=-1
675 # The AST transformation makes this do a+=-1
675 ip.define_macro("amacro", "a+=1\nprint(a)")
676 ip.define_macro("amacro", "a+=1\nprint(a)")
676
677
677 with tt.AssertPrints("9"):
678 with tt.AssertPrints("9"):
678 ip.run_cell("amacro")
679 ip.run_cell("amacro")
679 with tt.AssertPrints("8"):
680 with tt.AssertPrints("8"):
680 ip.run_cell("amacro")
681 ip.run_cell("amacro")
681
682
682 class IntegerWrapper(ast.NodeTransformer):
683 class IntegerWrapper(ast.NodeTransformer):
683 """Wraps all integers in a call to Integer()"""
684 """Wraps all integers in a call to Integer()"""
685
686 # for Python 3.7 and earlier
687
688 # for Python 3.7 and earlier
684 def visit_Num(self, node):
689 def visit_Num(self, node):
685 if isinstance(node.n, int):
690 if isinstance(node.n, int):
686 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
691 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
687 args=[node], keywords=[])
692 args=[node], keywords=[])
688 return node
693 return node
689
694
690 if sys.version_info > (3,8):
695 # For Python 3.8+
691 def visit_Constant(self, node):
696 def visit_Constant(self, node):
692 if isinstance(node.value, int):
697 if isinstance(node.value, int):
693 return self.visit_Num(node)
698 return self.visit_Num(node)
694 return node
699 return node
695
700
696
701
697 class TestAstTransform2(unittest.TestCase):
702 class TestAstTransform2(unittest.TestCase):
698 def setUp(self):
703 def setUp(self):
699 self.intwrapper = IntegerWrapper()
704 self.intwrapper = IntegerWrapper()
700 ip.ast_transformers.append(self.intwrapper)
705 ip.ast_transformers.append(self.intwrapper)
701
706
702 self.calls = []
707 self.calls = []
703 def Integer(*args):
708 def Integer(*args):
704 self.calls.append(args)
709 self.calls.append(args)
705 return args
710 return args
706 ip.push({"Integer": Integer})
711 ip.push({"Integer": Integer})
707
712
708 def tearDown(self):
713 def tearDown(self):
709 ip.ast_transformers.remove(self.intwrapper)
714 ip.ast_transformers.remove(self.intwrapper)
710 del ip.user_ns['Integer']
715 del ip.user_ns['Integer']
711
716
712 def test_run_cell(self):
717 def test_run_cell(self):
713 ip.run_cell("n = 2")
718 ip.run_cell("n = 2")
714 self.assertEqual(self.calls, [(2,)])
719 self.assertEqual(self.calls, [(2,)])
715
720
716 # This shouldn't throw an error
721 # This shouldn't throw an error
717 ip.run_cell("o = 2.0")
722 ip.run_cell("o = 2.0")
718 self.assertEqual(ip.user_ns['o'], 2.0)
723 self.assertEqual(ip.user_ns['o'], 2.0)
719
724
720 def test_timeit(self):
725 def test_timeit(self):
721 called = set()
726 called = set()
722 def f(x):
727 def f(x):
723 called.add(x)
728 called.add(x)
724 ip.push({'f':f})
729 ip.push({'f':f})
725
730
726 with tt.AssertPrints("std. dev. of"):
731 with tt.AssertPrints("std. dev. of"):
727 ip.run_line_magic("timeit", "-n1 f(1)")
732 ip.run_line_magic("timeit", "-n1 f(1)")
728 self.assertEqual(called, {(1,)})
733 self.assertEqual(called, {(1,)})
729 called.clear()
734 called.clear()
730
735
731 with tt.AssertPrints("std. dev. of"):
736 with tt.AssertPrints("std. dev. of"):
732 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
737 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
733 self.assertEqual(called, {(2,), (3,)})
738 self.assertEqual(called, {(2,), (3,)})
734
739
735 class ErrorTransformer(ast.NodeTransformer):
740 class ErrorTransformer(ast.NodeTransformer):
736 """Throws an error when it sees a number."""
741 """Throws an error when it sees a number."""
742
743 # for Python 3.7 and earlier
737 def visit_Num(self, node):
744 def visit_Num(self, node):
738 raise ValueError("test")
745 raise ValueError("test")
739
746
740 if sys.version_info > (3,8):
747 # for Python 3.8+
741 def visit_Constant(self, node):
748 def visit_Constant(self, node):
742 if isinstance(node.value, int):
749 if isinstance(node.value, int):
743 return self.visit_Num(node)
750 return self.visit_Num(node)
744 return node
751 return node
745
752
746
753
747 class TestAstTransformError(unittest.TestCase):
754 class TestAstTransformError(unittest.TestCase):
748 def test_unregistering(self):
755 def test_unregistering(self):
749 err_transformer = ErrorTransformer()
756 err_transformer = ErrorTransformer()
750 ip.ast_transformers.append(err_transformer)
757 ip.ast_transformers.append(err_transformer)
751
758
752 with tt.AssertPrints("unregister", channel='stderr'):
759 with tt.AssertPrints("unregister", channel='stderr'):
753 ip.run_cell("1 + 2")
760 ip.run_cell("1 + 2")
754
761
755 # This should have been removed.
762 # This should have been removed.
756 nt.assert_not_in(err_transformer, ip.ast_transformers)
763 nt.assert_not_in(err_transformer, ip.ast_transformers)
757
764
758
765
759 class StringRejector(ast.NodeTransformer):
766 class StringRejector(ast.NodeTransformer):
760 """Throws an InputRejected when it sees a string literal.
767 """Throws an InputRejected when it sees a string literal.
761
768
762 Used to verify that NodeTransformers can signal that a piece of code should
769 Used to verify that NodeTransformers can signal that a piece of code should
763 not be executed by throwing an InputRejected.
770 not be executed by throwing an InputRejected.
764 """
771 """
765
772
773 #for python 3.7 and earlier
766 def visit_Str(self, node):
774 def visit_Str(self, node):
767 raise InputRejected("test")
775 raise InputRejected("test")
768
776
769 # 3.8 only
777 # 3.8 only
770 def visit_Constant(self, node):
778 def visit_Constant(self, node):
771 if isinstance(node.value, str):
779 if isinstance(node.value, str):
772 raise InputRejected("test")
780 raise InputRejected("test")
773 return node
781 return node
774
782
775
783
776 class TestAstTransformInputRejection(unittest.TestCase):
784 class TestAstTransformInputRejection(unittest.TestCase):
777
785
778 def setUp(self):
786 def setUp(self):
779 self.transformer = StringRejector()
787 self.transformer = StringRejector()
780 ip.ast_transformers.append(self.transformer)
788 ip.ast_transformers.append(self.transformer)
781
789
782 def tearDown(self):
790 def tearDown(self):
783 ip.ast_transformers.remove(self.transformer)
791 ip.ast_transformers.remove(self.transformer)
784
792
785 def test_input_rejection(self):
793 def test_input_rejection(self):
786 """Check that NodeTransformers can reject input."""
794 """Check that NodeTransformers can reject input."""
787
795
788 expect_exception_tb = tt.AssertPrints("InputRejected: test")
796 expect_exception_tb = tt.AssertPrints("InputRejected: test")
789 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
797 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
790
798
791 # Run the same check twice to verify that the transformer is not
799 # Run the same check twice to verify that the transformer is not
792 # disabled after raising.
800 # disabled after raising.
793 with expect_exception_tb, expect_no_cell_output:
801 with expect_exception_tb, expect_no_cell_output:
794 ip.run_cell("'unsafe'")
802 ip.run_cell("'unsafe'")
795
803
796 with expect_exception_tb, expect_no_cell_output:
804 with expect_exception_tb, expect_no_cell_output:
797 res = ip.run_cell("'unsafe'")
805 res = ip.run_cell("'unsafe'")
798
806
799 self.assertIsInstance(res.error_before_exec, InputRejected)
807 self.assertIsInstance(res.error_before_exec, InputRejected)
800
808
801 def test__IPYTHON__():
809 def test__IPYTHON__():
802 # This shouldn't raise a NameError, that's all
810 # This shouldn't raise a NameError, that's all
803 __IPYTHON__
811 __IPYTHON__
804
812
805
813
806 class DummyRepr(object):
814 class DummyRepr(object):
807 def __repr__(self):
815 def __repr__(self):
808 return "DummyRepr"
816 return "DummyRepr"
809
817
810 def _repr_html_(self):
818 def _repr_html_(self):
811 return "<b>dummy</b>"
819 return "<b>dummy</b>"
812
820
813 def _repr_javascript_(self):
821 def _repr_javascript_(self):
814 return "console.log('hi');", {'key': 'value'}
822 return "console.log('hi');", {'key': 'value'}
815
823
816
824
817 def test_user_variables():
825 def test_user_variables():
818 # enable all formatters
826 # enable all formatters
819 ip.display_formatter.active_types = ip.display_formatter.format_types
827 ip.display_formatter.active_types = ip.display_formatter.format_types
820
828
821 ip.user_ns['dummy'] = d = DummyRepr()
829 ip.user_ns['dummy'] = d = DummyRepr()
822 keys = {'dummy', 'doesnotexist'}
830 keys = {'dummy', 'doesnotexist'}
823 r = ip.user_expressions({ key:key for key in keys})
831 r = ip.user_expressions({ key:key for key in keys})
824
832
825 nt.assert_equal(keys, set(r.keys()))
833 nt.assert_equal(keys, set(r.keys()))
826 dummy = r['dummy']
834 dummy = r['dummy']
827 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
835 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
828 nt.assert_equal(dummy['status'], 'ok')
836 nt.assert_equal(dummy['status'], 'ok')
829 data = dummy['data']
837 data = dummy['data']
830 metadata = dummy['metadata']
838 metadata = dummy['metadata']
831 nt.assert_equal(data.get('text/html'), d._repr_html_())
839 nt.assert_equal(data.get('text/html'), d._repr_html_())
832 js, jsmd = d._repr_javascript_()
840 js, jsmd = d._repr_javascript_()
833 nt.assert_equal(data.get('application/javascript'), js)
841 nt.assert_equal(data.get('application/javascript'), js)
834 nt.assert_equal(metadata.get('application/javascript'), jsmd)
842 nt.assert_equal(metadata.get('application/javascript'), jsmd)
835
843
836 dne = r['doesnotexist']
844 dne = r['doesnotexist']
837 nt.assert_equal(dne['status'], 'error')
845 nt.assert_equal(dne['status'], 'error')
838 nt.assert_equal(dne['ename'], 'NameError')
846 nt.assert_equal(dne['ename'], 'NameError')
839
847
840 # back to text only
848 # back to text only
841 ip.display_formatter.active_types = ['text/plain']
849 ip.display_formatter.active_types = ['text/plain']
842
850
843 def test_user_expression():
851 def test_user_expression():
844 # enable all formatters
852 # enable all formatters
845 ip.display_formatter.active_types = ip.display_formatter.format_types
853 ip.display_formatter.active_types = ip.display_formatter.format_types
846 query = {
854 query = {
847 'a' : '1 + 2',
855 'a' : '1 + 2',
848 'b' : '1/0',
856 'b' : '1/0',
849 }
857 }
850 r = ip.user_expressions(query)
858 r = ip.user_expressions(query)
851 import pprint
859 import pprint
852 pprint.pprint(r)
860 pprint.pprint(r)
853 nt.assert_equal(set(r.keys()), set(query.keys()))
861 nt.assert_equal(set(r.keys()), set(query.keys()))
854 a = r['a']
862 a = r['a']
855 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
863 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
856 nt.assert_equal(a['status'], 'ok')
864 nt.assert_equal(a['status'], 'ok')
857 data = a['data']
865 data = a['data']
858 metadata = a['metadata']
866 metadata = a['metadata']
859 nt.assert_equal(data.get('text/plain'), '3')
867 nt.assert_equal(data.get('text/plain'), '3')
860
868
861 b = r['b']
869 b = r['b']
862 nt.assert_equal(b['status'], 'error')
870 nt.assert_equal(b['status'], 'error')
863 nt.assert_equal(b['ename'], 'ZeroDivisionError')
871 nt.assert_equal(b['ename'], 'ZeroDivisionError')
864
872
865 # back to text only
873 # back to text only
866 ip.display_formatter.active_types = ['text/plain']
874 ip.display_formatter.active_types = ['text/plain']
867
875
868
876
869
877
870
878
871
879
872 class TestSyntaxErrorTransformer(unittest.TestCase):
880 class TestSyntaxErrorTransformer(unittest.TestCase):
873 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
881 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
874
882
875 @staticmethod
883 @staticmethod
876 def transformer(lines):
884 def transformer(lines):
877 for line in lines:
885 for line in lines:
878 pos = line.find('syntaxerror')
886 pos = line.find('syntaxerror')
879 if pos >= 0:
887 if pos >= 0:
880 e = SyntaxError('input contains "syntaxerror"')
888 e = SyntaxError('input contains "syntaxerror"')
881 e.text = line
889 e.text = line
882 e.offset = pos + 1
890 e.offset = pos + 1
883 raise e
891 raise e
884 return lines
892 return lines
885
893
886 def setUp(self):
894 def setUp(self):
887 ip.input_transformers_post.append(self.transformer)
895 ip.input_transformers_post.append(self.transformer)
888
896
889 def tearDown(self):
897 def tearDown(self):
890 ip.input_transformers_post.remove(self.transformer)
898 ip.input_transformers_post.remove(self.transformer)
891
899
892 def test_syntaxerror_input_transformer(self):
900 def test_syntaxerror_input_transformer(self):
893 with tt.AssertPrints('1234'):
901 with tt.AssertPrints('1234'):
894 ip.run_cell('1234')
902 ip.run_cell('1234')
895 with tt.AssertPrints('SyntaxError: invalid syntax'):
903 with tt.AssertPrints('SyntaxError: invalid syntax'):
896 ip.run_cell('1 2 3') # plain python syntax error
904 ip.run_cell('1 2 3') # plain python syntax error
897 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
905 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
898 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
906 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
899 with tt.AssertPrints('3456'):
907 with tt.AssertPrints('3456'):
900 ip.run_cell('3456')
908 ip.run_cell('3456')
901
909
902
910
903
911
904 def test_warning_suppression():
912 def test_warning_suppression():
905 ip.run_cell("import warnings")
913 ip.run_cell("import warnings")
906 try:
914 try:
907 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
915 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
908 ip.run_cell("warnings.warn('asdf')")
916 ip.run_cell("warnings.warn('asdf')")
909 # Here's the real test -- if we run that again, we should get the
917 # Here's the real test -- if we run that again, we should get the
910 # warning again. Traditionally, each warning was only issued once per
918 # warning again. Traditionally, each warning was only issued once per
911 # IPython session (approximately), even if the user typed in new and
919 # IPython session (approximately), even if the user typed in new and
912 # different code that should have also triggered the warning, leading
920 # different code that should have also triggered the warning, leading
913 # to much confusion.
921 # to much confusion.
914 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
922 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
915 ip.run_cell("warnings.warn('asdf')")
923 ip.run_cell("warnings.warn('asdf')")
916 finally:
924 finally:
917 ip.run_cell("del warnings")
925 ip.run_cell("del warnings")
918
926
919
927
920 def test_deprecation_warning():
928 def test_deprecation_warning():
921 ip.run_cell("""
929 ip.run_cell("""
922 import warnings
930 import warnings
923 def wrn():
931 def wrn():
924 warnings.warn(
932 warnings.warn(
925 "I AM A WARNING",
933 "I AM A WARNING",
926 DeprecationWarning
934 DeprecationWarning
927 )
935 )
928 """)
936 """)
929 try:
937 try:
930 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
938 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
931 ip.run_cell("wrn()")
939 ip.run_cell("wrn()")
932 finally:
940 finally:
933 ip.run_cell("del warnings")
941 ip.run_cell("del warnings")
934 ip.run_cell("del wrn")
942 ip.run_cell("del wrn")
935
943
936
944
937 class TestImportNoDeprecate(tt.TempFileMixin):
945 class TestImportNoDeprecate(tt.TempFileMixin):
938
946
939 def setup(self):
947 def setup(self):
940 """Make a valid python temp file."""
948 """Make a valid python temp file."""
941 self.mktmp("""
949 self.mktmp("""
942 import warnings
950 import warnings
943 def wrn():
951 def wrn():
944 warnings.warn(
952 warnings.warn(
945 "I AM A WARNING",
953 "I AM A WARNING",
946 DeprecationWarning
954 DeprecationWarning
947 )
955 )
948 """)
956 """)
949
957
950 def test_no_dep(self):
958 def test_no_dep(self):
951 """
959 """
952 No deprecation warning should be raised from imported functions
960 No deprecation warning should be raised from imported functions
953 """
961 """
954 ip.run_cell("from {} import wrn".format(self.fname))
962 ip.run_cell("from {} import wrn".format(self.fname))
955
963
956 with tt.AssertNotPrints("I AM A WARNING"):
964 with tt.AssertNotPrints("I AM A WARNING"):
957 ip.run_cell("wrn()")
965 ip.run_cell("wrn()")
958 ip.run_cell("del wrn")
966 ip.run_cell("del wrn")
959
967
960
968
961 def test_custom_exc_count():
969 def test_custom_exc_count():
962 hook = mock.Mock(return_value=None)
970 hook = mock.Mock(return_value=None)
963 ip.set_custom_exc((SyntaxError,), hook)
971 ip.set_custom_exc((SyntaxError,), hook)
964 before = ip.execution_count
972 before = ip.execution_count
965 ip.run_cell("def foo()", store_history=True)
973 ip.run_cell("def foo()", store_history=True)
966 # restore default excepthook
974 # restore default excepthook
967 ip.set_custom_exc((), None)
975 ip.set_custom_exc((), None)
968 nt.assert_equal(hook.call_count, 1)
976 nt.assert_equal(hook.call_count, 1)
969 nt.assert_equal(ip.execution_count, before + 1)
977 nt.assert_equal(ip.execution_count, before + 1)
970
978
971
979
972 def test_run_cell_async():
980 def test_run_cell_async():
973 loop = asyncio.get_event_loop()
981 loop = asyncio.get_event_loop()
974 ip.run_cell("import asyncio")
982 ip.run_cell("import asyncio")
975 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
983 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
976 assert asyncio.iscoroutine(coro)
984 assert asyncio.iscoroutine(coro)
977 result = loop.run_until_complete(coro)
985 result = loop.run_until_complete(coro)
978 assert isinstance(result, interactiveshell.ExecutionResult)
986 assert isinstance(result, interactiveshell.ExecutionResult)
979 assert result.result == 5
987 assert result.result == 5
980
988
981
989
982 def test_should_run_async():
990 def test_should_run_async():
983 assert not ip.should_run_async("a = 5")
991 assert not ip.should_run_async("a = 5")
984 assert ip.should_run_async("await x")
992 assert ip.should_run_async("await x")
985 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
993 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
@@ -1,408 +1,412 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import logging
5 import logging
6 import sys
6 import sys
7 import os.path
7 import os.path
8 from textwrap import dedent
8 from textwrap import dedent
9 import traceback
9 import traceback
10 import unittest
10 import unittest
11 from unittest import mock
11 from unittest import mock
12
12
13 from ..ultratb import ColorTB, VerboseTB, find_recursion
13 from ..ultratb import ColorTB, VerboseTB, find_recursion
14
14
15
15
16 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
17 from IPython.testing.decorators import onlyif_unicode_paths
17 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.tempdir import TemporaryDirectory
19 from IPython.utils.tempdir import TemporaryDirectory
20
20
21 ip = get_ipython()
21 ip = get_ipython()
22
22
23 file_1 = """1
23 file_1 = """1
24 2
24 2
25 3
25 3
26 def f():
26 def f():
27 1/0
27 1/0
28 """
28 """
29
29
30 file_2 = """def f():
30 file_2 = """def f():
31 1/0
31 1/0
32 """
32 """
33
33
34 class ChangedPyFileTest(unittest.TestCase):
34 class ChangedPyFileTest(unittest.TestCase):
35 def test_changing_py_file(self):
35 def test_changing_py_file(self):
36 """Traceback produced if the line where the error occurred is missing?
36 """Traceback produced if the line where the error occurred is missing?
37
37
38 https://github.com/ipython/ipython/issues/1456
38 https://github.com/ipython/ipython/issues/1456
39 """
39 """
40 with TemporaryDirectory() as td:
40 with TemporaryDirectory() as td:
41 fname = os.path.join(td, "foo.py")
41 fname = os.path.join(td, "foo.py")
42 with open(fname, "w") as f:
42 with open(fname, "w") as f:
43 f.write(file_1)
43 f.write(file_1)
44
44
45 with prepended_to_syspath(td):
45 with prepended_to_syspath(td):
46 ip.run_cell("import foo")
46 ip.run_cell("import foo")
47
47
48 with tt.AssertPrints("ZeroDivisionError"):
48 with tt.AssertPrints("ZeroDivisionError"):
49 ip.run_cell("foo.f()")
49 ip.run_cell("foo.f()")
50
50
51 # Make the file shorter, so the line of the error is missing.
51 # Make the file shorter, so the line of the error is missing.
52 with open(fname, "w") as f:
52 with open(fname, "w") as f:
53 f.write(file_2)
53 f.write(file_2)
54
54
55 # For some reason, this was failing on the *second* call after
55 # For some reason, this was failing on the *second* call after
56 # changing the file, so we call f() twice.
56 # changing the file, so we call f() twice.
57 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
57 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
58 with tt.AssertPrints("ZeroDivisionError"):
58 with tt.AssertPrints("ZeroDivisionError"):
59 ip.run_cell("foo.f()")
59 ip.run_cell("foo.f()")
60 with tt.AssertPrints("ZeroDivisionError"):
60 with tt.AssertPrints("ZeroDivisionError"):
61 ip.run_cell("foo.f()")
61 ip.run_cell("foo.f()")
62
62
63 iso_8859_5_file = u'''# coding: iso-8859-5
63 iso_8859_5_file = u'''# coding: iso-8859-5
64
64
65 def fail():
65 def fail():
66 """Π΄Π±Π˜Π–"""
66 """Π΄Π±Π˜Π–"""
67 1/0 # Π΄Π±Π˜Π–
67 1/0 # Π΄Π±Π˜Π–
68 '''
68 '''
69
69
70 class NonAsciiTest(unittest.TestCase):
70 class NonAsciiTest(unittest.TestCase):
71 @onlyif_unicode_paths
71 @onlyif_unicode_paths
72 def test_nonascii_path(self):
72 def test_nonascii_path(self):
73 # Non-ascii directory name as well.
73 # Non-ascii directory name as well.
74 with TemporaryDirectory(suffix=u'Γ©') as td:
74 with TemporaryDirectory(suffix=u'Γ©') as td:
75 fname = os.path.join(td, u"fooΓ©.py")
75 fname = os.path.join(td, u"fooΓ©.py")
76 with open(fname, "w") as f:
76 with open(fname, "w") as f:
77 f.write(file_1)
77 f.write(file_1)
78
78
79 with prepended_to_syspath(td):
79 with prepended_to_syspath(td):
80 ip.run_cell("import foo")
80 ip.run_cell("import foo")
81
81
82 with tt.AssertPrints("ZeroDivisionError"):
82 with tt.AssertPrints("ZeroDivisionError"):
83 ip.run_cell("foo.f()")
83 ip.run_cell("foo.f()")
84
84
85 def test_iso8859_5(self):
85 def test_iso8859_5(self):
86 with TemporaryDirectory() as td:
86 with TemporaryDirectory() as td:
87 fname = os.path.join(td, 'dfghjkl.py')
87 fname = os.path.join(td, 'dfghjkl.py')
88
88
89 with io.open(fname, 'w', encoding='iso-8859-5') as f:
89 with io.open(fname, 'w', encoding='iso-8859-5') as f:
90 f.write(iso_8859_5_file)
90 f.write(iso_8859_5_file)
91
91
92 with prepended_to_syspath(td):
92 with prepended_to_syspath(td):
93 ip.run_cell("from dfghjkl import fail")
93 ip.run_cell("from dfghjkl import fail")
94
94
95 with tt.AssertPrints("ZeroDivisionError"):
95 with tt.AssertPrints("ZeroDivisionError"):
96 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
96 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
97 ip.run_cell('fail()')
97 ip.run_cell('fail()')
98
98
99 def test_nonascii_msg(self):
99 def test_nonascii_msg(self):
100 cell = u"raise Exception('Γ©')"
100 cell = u"raise Exception('Γ©')"
101 expected = u"Exception('Γ©')"
101 expected = u"Exception('Γ©')"
102 ip.run_cell("%xmode plain")
102 ip.run_cell("%xmode plain")
103 with tt.AssertPrints(expected):
103 with tt.AssertPrints(expected):
104 ip.run_cell(cell)
104 ip.run_cell(cell)
105
105
106 ip.run_cell("%xmode verbose")
106 ip.run_cell("%xmode verbose")
107 with tt.AssertPrints(expected):
107 with tt.AssertPrints(expected):
108 ip.run_cell(cell)
108 ip.run_cell(cell)
109
109
110 ip.run_cell("%xmode context")
110 ip.run_cell("%xmode context")
111 with tt.AssertPrints(expected):
111 with tt.AssertPrints(expected):
112 ip.run_cell(cell)
112 ip.run_cell(cell)
113
113
114 ip.run_cell("%xmode minimal")
114 ip.run_cell("%xmode minimal")
115 with tt.AssertPrints(u"Exception: Γ©"):
115 with tt.AssertPrints(u"Exception: Γ©"):
116 ip.run_cell(cell)
116 ip.run_cell(cell)
117
117
118 # Put this back into Context mode for later tests.
118 # Put this back into Context mode for later tests.
119 ip.run_cell("%xmode context")
119 ip.run_cell("%xmode context")
120
120
121 class NestedGenExprTestCase(unittest.TestCase):
121 class NestedGenExprTestCase(unittest.TestCase):
122 """
122 """
123 Regression test for the following issues:
123 Regression test for the following issues:
124 https://github.com/ipython/ipython/issues/8293
124 https://github.com/ipython/ipython/issues/8293
125 https://github.com/ipython/ipython/issues/8205
125 https://github.com/ipython/ipython/issues/8205
126 """
126 """
127 def test_nested_genexpr(self):
127 def test_nested_genexpr(self):
128 code = dedent(
128 code = dedent(
129 """\
129 """\
130 class SpecificException(Exception):
130 class SpecificException(Exception):
131 pass
131 pass
132
132
133 def foo(x):
133 def foo(x):
134 raise SpecificException("Success!")
134 raise SpecificException("Success!")
135
135
136 sum(sum(foo(x) for _ in [0]) for x in [0])
136 sum(sum(foo(x) for _ in [0]) for x in [0])
137 """
137 """
138 )
138 )
139 with tt.AssertPrints('SpecificException: Success!', suppress=False):
139 with tt.AssertPrints('SpecificException: Success!', suppress=False):
140 ip.run_cell(code)
140 ip.run_cell(code)
141
141
142
142
143 indentationerror_file = """if True:
143 indentationerror_file = """if True:
144 zoon()
144 zoon()
145 """
145 """
146
146
147 class IndentationErrorTest(unittest.TestCase):
147 class IndentationErrorTest(unittest.TestCase):
148 def test_indentationerror_shows_line(self):
148 def test_indentationerror_shows_line(self):
149 # See issue gh-2398
149 # See issue gh-2398
150 with tt.AssertPrints("IndentationError"):
150 with tt.AssertPrints("IndentationError"):
151 with tt.AssertPrints("zoon()", suppress=False):
151 with tt.AssertPrints("zoon()", suppress=False):
152 ip.run_cell(indentationerror_file)
152 ip.run_cell(indentationerror_file)
153
153
154 with TemporaryDirectory() as td:
154 with TemporaryDirectory() as td:
155 fname = os.path.join(td, "foo.py")
155 fname = os.path.join(td, "foo.py")
156 with open(fname, "w") as f:
156 with open(fname, "w") as f:
157 f.write(indentationerror_file)
157 f.write(indentationerror_file)
158
158
159 with tt.AssertPrints("IndentationError"):
159 with tt.AssertPrints("IndentationError"):
160 with tt.AssertPrints("zoon()", suppress=False):
160 with tt.AssertPrints("zoon()", suppress=False):
161 ip.magic('run %s' % fname)
161 ip.magic('run %s' % fname)
162
162
163 se_file_1 = """1
163 se_file_1 = """1
164 2
164 2
165 7/
165 7/
166 """
166 """
167
167
168 se_file_2 = """7/
168 se_file_2 = """7/
169 """
169 """
170
170
171 class SyntaxErrorTest(unittest.TestCase):
171 class SyntaxErrorTest(unittest.TestCase):
172 def test_syntaxerror_without_lineno(self):
172 def test_syntaxerror_without_lineno(self):
173 with tt.AssertNotPrints("TypeError"):
173 with tt.AssertNotPrints("TypeError"):
174 with tt.AssertPrints("line unknown"):
174 with tt.AssertPrints("line unknown"):
175 ip.run_cell("raise SyntaxError()")
175 ip.run_cell("raise SyntaxError()")
176
176
177 def test_syntaxerror_no_stacktrace_at_compile_time(self):
177 def test_syntaxerror_no_stacktrace_at_compile_time(self):
178 syntax_error_at_compile_time = """
178 syntax_error_at_compile_time = """
179 def foo():
179 def foo():
180 ..
180 ..
181 """
181 """
182 with tt.AssertPrints("SyntaxError"):
182 with tt.AssertPrints("SyntaxError"):
183 ip.run_cell(syntax_error_at_compile_time)
183 ip.run_cell(syntax_error_at_compile_time)
184
184
185 with tt.AssertNotPrints("foo()"):
185 with tt.AssertNotPrints("foo()"):
186 ip.run_cell(syntax_error_at_compile_time)
186 ip.run_cell(syntax_error_at_compile_time)
187
187
188 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
188 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
189 syntax_error_at_runtime = """
189 syntax_error_at_runtime = """
190 def foo():
190 def foo():
191 eval("..")
191 eval("..")
192
192
193 def bar():
193 def bar():
194 foo()
194 foo()
195
195
196 bar()
196 bar()
197 """
197 """
198 with tt.AssertPrints("SyntaxError"):
198 with tt.AssertPrints("SyntaxError"):
199 ip.run_cell(syntax_error_at_runtime)
199 ip.run_cell(syntax_error_at_runtime)
200 # Assert syntax error during runtime generate stacktrace
200 # Assert syntax error during runtime generate stacktrace
201 with tt.AssertPrints(["foo()", "bar()"]):
201 with tt.AssertPrints(["foo()", "bar()"]):
202 ip.run_cell(syntax_error_at_runtime)
202 ip.run_cell(syntax_error_at_runtime)
203
203
204 def test_changing_py_file(self):
204 def test_changing_py_file(self):
205 with TemporaryDirectory() as td:
205 with TemporaryDirectory() as td:
206 fname = os.path.join(td, "foo.py")
206 fname = os.path.join(td, "foo.py")
207 with open(fname, 'w') as f:
207 with open(fname, 'w') as f:
208 f.write(se_file_1)
208 f.write(se_file_1)
209
209
210 with tt.AssertPrints(["7/", "SyntaxError"]):
210 with tt.AssertPrints(["7/", "SyntaxError"]):
211 ip.magic("run " + fname)
211 ip.magic("run " + fname)
212
212
213 # Modify the file
213 # Modify the file
214 with open(fname, 'w') as f:
214 with open(fname, 'w') as f:
215 f.write(se_file_2)
215 f.write(se_file_2)
216
216
217 # The SyntaxError should point to the correct line
217 # The SyntaxError should point to the correct line
218 with tt.AssertPrints(["7/", "SyntaxError"]):
218 with tt.AssertPrints(["7/", "SyntaxError"]):
219 ip.magic("run " + fname)
219 ip.magic("run " + fname)
220
220
221 def test_non_syntaxerror(self):
221 def test_non_syntaxerror(self):
222 # SyntaxTB may be called with an error other than a SyntaxError
222 # SyntaxTB may be called with an error other than a SyntaxError
223 # See e.g. gh-4361
223 # See e.g. gh-4361
224 try:
224 try:
225 raise ValueError('QWERTY')
225 raise ValueError('QWERTY')
226 except ValueError:
226 except ValueError:
227 with tt.AssertPrints('QWERTY'):
227 with tt.AssertPrints('QWERTY'):
228 ip.showsyntaxerror()
228 ip.showsyntaxerror()
229
229
230
230
231 class Python3ChainedExceptionsTest(unittest.TestCase):
231 class Python3ChainedExceptionsTest(unittest.TestCase):
232 DIRECT_CAUSE_ERROR_CODE = """
232 DIRECT_CAUSE_ERROR_CODE = """
233 try:
233 try:
234 x = 1 + 2
234 x = 1 + 2
235 print(not_defined_here)
235 print(not_defined_here)
236 except Exception as e:
236 except Exception as e:
237 x += 55
237 x += 55
238 x - 1
238 x - 1
239 y = {}
239 y = {}
240 raise KeyError('uh') from e
240 raise KeyError('uh') from e
241 """
241 """
242
242
243 EXCEPTION_DURING_HANDLING_CODE = """
243 EXCEPTION_DURING_HANDLING_CODE = """
244 try:
244 try:
245 x = 1 + 2
245 x = 1 + 2
246 print(not_defined_here)
246 print(not_defined_here)
247 except Exception as e:
247 except Exception as e:
248 x += 55
248 x += 55
249 x - 1
249 x - 1
250 y = {}
250 y = {}
251 raise KeyError('uh')
251 raise KeyError('uh')
252 """
252 """
253
253
254 SUPPRESS_CHAINING_CODE = """
254 SUPPRESS_CHAINING_CODE = """
255 try:
255 try:
256 1/0
256 1/0
257 except Exception:
257 except Exception:
258 raise ValueError("Yikes") from None
258 raise ValueError("Yikes") from None
259 """
259 """
260
260
261 def test_direct_cause_error(self):
261 def test_direct_cause_error(self):
262 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
262 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
263 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
263 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
264
264
265 def test_exception_during_handling_error(self):
265 def test_exception_during_handling_error(self):
266 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
266 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
267 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
267 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
268
268
269 def test_suppress_exception_chaining(self):
269 def test_suppress_exception_chaining(self):
270 with tt.AssertNotPrints("ZeroDivisionError"), \
270 with tt.AssertNotPrints("ZeroDivisionError"), \
271 tt.AssertPrints("ValueError", suppress=False):
271 tt.AssertPrints("ValueError", suppress=False):
272 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
272 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
273
273
274
274
275 class RecursionTest(unittest.TestCase):
275 class RecursionTest(unittest.TestCase):
276 DEFINITIONS = """
276 DEFINITIONS = """
277 def non_recurs():
277 def non_recurs():
278 1/0
278 1/0
279
279
280 def r1():
280 def r1():
281 r1()
281 r1()
282
282
283 def r3a():
283 def r3a():
284 r3b()
284 r3b()
285
285
286 def r3b():
286 def r3b():
287 r3c()
287 r3c()
288
288
289 def r3c():
289 def r3c():
290 r3a()
290 r3a()
291
291
292 def r3o1():
292 def r3o1():
293 r3a()
293 r3a()
294
294
295 def r3o2():
295 def r3o2():
296 r3o1()
296 r3o1()
297 """
297 """
298 def setUp(self):
298 def setUp(self):
299 ip.run_cell(self.DEFINITIONS)
299 ip.run_cell(self.DEFINITIONS)
300
300
301 def test_no_recursion(self):
301 def test_no_recursion(self):
302 with tt.AssertNotPrints("frames repeated"):
302 with tt.AssertNotPrints("frames repeated"):
303 ip.run_cell("non_recurs()")
303 ip.run_cell("non_recurs()")
304
304
305 def test_recursion_one_frame(self):
305 def test_recursion_one_frame(self):
306 with tt.AssertPrints("1 frames repeated"):
306 with tt.AssertPrints("1 frames repeated"):
307 ip.run_cell("r1()")
307 ip.run_cell("r1()")
308
308
309 def test_recursion_three_frames(self):
309 def test_recursion_three_frames(self):
310 with tt.AssertPrints("3 frames repeated"):
310 with tt.AssertPrints("3 frames repeated"):
311 ip.run_cell("r3o2()")
311 ip.run_cell("r3o2()")
312
312
313 def test_find_recursion(self):
313 def test_find_recursion(self):
314 captured = []
314 captured = []
315 def capture_exc(*args, **kwargs):
315 def capture_exc(*args, **kwargs):
316 captured.append(sys.exc_info())
316 captured.append(sys.exc_info())
317 with mock.patch.object(ip, 'showtraceback', capture_exc):
317 with mock.patch.object(ip, 'showtraceback', capture_exc):
318 ip.run_cell("r3o2()")
318 ip.run_cell("r3o2()")
319
319
320 self.assertEqual(len(captured), 1)
320 self.assertEqual(len(captured), 1)
321 etype, evalue, tb = captured[0]
321 etype, evalue, tb = captured[0]
322 self.assertIn("recursion", str(evalue))
322 self.assertIn("recursion", str(evalue))
323
323
324 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
324 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
325 for r in records[:10]:
325 for r in records[:10]:
326 print(r[1:4])
326 print(r[1:4])
327
327
328 # The outermost frames should be:
328 # The outermost frames should be:
329 # 0: the 'cell' that was running when the exception came up
329 # 0: the 'cell' that was running when the exception came up
330 # 1: r3o2()
330 # 1: r3o2()
331 # 2: r3o1()
331 # 2: r3o1()
332 # 3: r3a()
332 # 3: r3a()
333 # Then repeating r3b, r3c, r3a
333 # Then repeating r3b, r3c, r3a
334 last_unique, repeat_length = find_recursion(etype, evalue, records)
334 last_unique, repeat_length = find_recursion(etype, evalue, records)
335 self.assertEqual(last_unique, 2)
335 self.assertEqual(last_unique, 2)
336 self.assertEqual(repeat_length, 3)
336 self.assertEqual(repeat_length, 3)
337
337
338
338
339 #----------------------------------------------------------------------------
339 #----------------------------------------------------------------------------
340
340
341 # module testing (minimal)
341 # module testing (minimal)
342 def test_handlers():
342 def test_handlers():
343 def spam(c, d_e):
343 def spam(c, d_e):
344 (d, e) = d_e
344 (d, e) = d_e
345 x = c + d
345 x = c + d
346 y = c * d
346 y = c * d
347 foo(x, y)
347 foo(x, y)
348
348
349 def foo(a, b, bar=1):
349 def foo(a, b, bar=1):
350 eggs(a, b + bar)
350 eggs(a, b + bar)
351
351
352 def eggs(f, g, z=globals()):
352 def eggs(f, g, z=globals()):
353 h = f + g
353 h = f + g
354 i = f - g
354 i = f - g
355 return h / i
355 return h / i
356
356
357 buff = io.StringIO()
357 buff = io.StringIO()
358
358
359 buff.write('')
359 buff.write('')
360 buff.write('*** Before ***')
360 buff.write('*** Before ***')
361 try:
361 try:
362 buff.write(spam(1, (2, 3)))
362 buff.write(spam(1, (2, 3)))
363 except:
363 except:
364 traceback.print_exc(file=buff)
364 traceback.print_exc(file=buff)
365
365
366 handler = ColorTB(ostream=buff)
366 handler = ColorTB(ostream=buff)
367 buff.write('*** ColorTB ***')
367 buff.write('*** ColorTB ***')
368 try:
368 try:
369 buff.write(spam(1, (2, 3)))
369 buff.write(spam(1, (2, 3)))
370 except:
370 except:
371 handler(*sys.exc_info())
371 handler(*sys.exc_info())
372 buff.write('')
372 buff.write('')
373
373
374 handler = VerboseTB(ostream=buff)
374 handler = VerboseTB(ostream=buff)
375 buff.write('*** VerboseTB ***')
375 buff.write('*** VerboseTB ***')
376 try:
376 try:
377 buff.write(spam(1, (2, 3)))
377 buff.write(spam(1, (2, 3)))
378 except:
378 except:
379 handler(*sys.exc_info())
379 handler(*sys.exc_info())
380 buff.write('')
380 buff.write('')
381
381
382 from IPython.testing.decorators import skipif
382 from IPython.testing.decorators import skipif
383
383
384 class TokenizeFailureTest(unittest.TestCase):
384 class TokenizeFailureTest(unittest.TestCase):
385 """Tests related to https://github.com/ipython/ipython/issues/6864."""
385 """Tests related to https://github.com/ipython/ipython/issues/6864."""
386
386
387 # that appear to test that we are handling an exception that can be thrown
388 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
389 # I'm unsure if other sequences can make it raise this error. Let's just
390 # skip in 3.8 for now
387 @skipif(sys.version_info > (3,8))
391 @skipif(sys.version_info > (3,8))
388 def testLogging(self):
392 def testLogging(self):
389 message = "An unexpected error occurred while tokenizing input"
393 message = "An unexpected error occurred while tokenizing input"
390 cell = 'raise ValueError("""a\nb""")'
394 cell = 'raise ValueError("""a\nb""")'
391
395
392 stream = io.StringIO()
396 stream = io.StringIO()
393 handler = logging.StreamHandler(stream)
397 handler = logging.StreamHandler(stream)
394 logger = logging.getLogger()
398 logger = logging.getLogger()
395 loglevel = logger.level
399 loglevel = logger.level
396 logger.addHandler(handler)
400 logger.addHandler(handler)
397 self.addCleanup(lambda: logger.removeHandler(handler))
401 self.addCleanup(lambda: logger.removeHandler(handler))
398 self.addCleanup(lambda: logger.setLevel(loglevel))
402 self.addCleanup(lambda: logger.setLevel(loglevel))
399
403
400 logger.setLevel(logging.INFO)
404 logger.setLevel(logging.INFO)
401 with tt.AssertNotPrints(message):
405 with tt.AssertNotPrints(message):
402 ip.run_cell(cell)
406 ip.run_cell(cell)
403 self.assertNotIn(message, stream.getvalue())
407 self.assertNotIn(message, stream.getvalue())
404
408
405 logger.setLevel(logging.DEBUG)
409 logger.setLevel(logging.DEBUG)
406 with tt.AssertNotPrints(message):
410 with tt.AssertNotPrints(message):
407 ip.run_cell(cell)
411 ip.run_cell(cell)
408 self.assertIn(message, stream.getvalue())
412 self.assertIn(message, stream.getvalue())
General Comments 0
You need to be logged in to leave comments. Login now