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