##// END OF EJS Templates
deprecated ast attribute (#14575)
M Bussonnier -
r28957:0615526f merge
parent child Browse files
Show More
@@ -1,1221 +1,1221
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 import pytest
20 import pytest
21 from unittest import mock
21 from unittest import mock
22
22
23 from os.path import join
23 from os.path import join
24
24
25 from IPython.core.error import InputRejected
25 from IPython.core.error import InputRejected
26 from IPython.core.inputtransformer import InputTransformer
26 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core import interactiveshell
27 from IPython.core import interactiveshell
28 from IPython.core.oinspect import OInfo
28 from IPython.core.oinspect import OInfo
29 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
30 skipif,
30 skipif,
31 skip_win32,
31 skip_win32,
32 onlyif_unicode_paths,
32 onlyif_unicode_paths,
33 onlyif_cmds_exist,
33 onlyif_cmds_exist,
34 skip_if_not_osx,
34 skip_if_not_osx,
35 )
35 )
36 from IPython.testing import tools as tt
36 from IPython.testing import tools as tt
37 from IPython.utils.process import find_cmd
37 from IPython.utils.process import find_cmd
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Globals
40 # Globals
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # This is used by every single test, no point repeating it ad nauseam
42 # This is used by every single test, no point repeating it ad nauseam
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # Tests
45 # Tests
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47
47
48 class DerivedInterrupt(KeyboardInterrupt):
48 class DerivedInterrupt(KeyboardInterrupt):
49 pass
49 pass
50
50
51 class InteractiveShellTestCase(unittest.TestCase):
51 class InteractiveShellTestCase(unittest.TestCase):
52 def test_naked_string_cells(self):
52 def test_naked_string_cells(self):
53 """Test that cells with only naked strings are fully executed"""
53 """Test that cells with only naked strings are fully executed"""
54 # First, single-line inputs
54 # First, single-line inputs
55 ip.run_cell('"a"\n')
55 ip.run_cell('"a"\n')
56 self.assertEqual(ip.user_ns['_'], 'a')
56 self.assertEqual(ip.user_ns['_'], 'a')
57 # And also multi-line cells
57 # And also multi-line cells
58 ip.run_cell('"""a\nb"""\n')
58 ip.run_cell('"""a\nb"""\n')
59 self.assertEqual(ip.user_ns['_'], 'a\nb')
59 self.assertEqual(ip.user_ns['_'], 'a\nb')
60
60
61 def test_run_empty_cell(self):
61 def test_run_empty_cell(self):
62 """Just make sure we don't get a horrible error with a blank
62 """Just make sure we don't get a horrible error with a blank
63 cell of input. Yes, I did overlook that."""
63 cell of input. Yes, I did overlook that."""
64 old_xc = ip.execution_count
64 old_xc = ip.execution_count
65 res = ip.run_cell('')
65 res = ip.run_cell('')
66 self.assertEqual(ip.execution_count, old_xc)
66 self.assertEqual(ip.execution_count, old_xc)
67 self.assertEqual(res.execution_count, None)
67 self.assertEqual(res.execution_count, None)
68
68
69 def test_run_cell_multiline(self):
69 def test_run_cell_multiline(self):
70 """Multi-block, multi-line cells must execute correctly.
70 """Multi-block, multi-line cells must execute correctly.
71 """
71 """
72 src = '\n'.join(["x=1",
72 src = '\n'.join(["x=1",
73 "y=2",
73 "y=2",
74 "if 1:",
74 "if 1:",
75 " x += 1",
75 " x += 1",
76 " y += 1",])
76 " y += 1",])
77 res = ip.run_cell(src)
77 res = ip.run_cell(src)
78 self.assertEqual(ip.user_ns['x'], 2)
78 self.assertEqual(ip.user_ns['x'], 2)
79 self.assertEqual(ip.user_ns['y'], 3)
79 self.assertEqual(ip.user_ns['y'], 3)
80 self.assertEqual(res.success, True)
80 self.assertEqual(res.success, True)
81 self.assertEqual(res.result, None)
81 self.assertEqual(res.result, None)
82
82
83 def test_multiline_string_cells(self):
83 def test_multiline_string_cells(self):
84 "Code sprinkled with multiline strings should execute (GH-306)"
84 "Code sprinkled with multiline strings should execute (GH-306)"
85 ip.run_cell('tmp=0')
85 ip.run_cell('tmp=0')
86 self.assertEqual(ip.user_ns['tmp'], 0)
86 self.assertEqual(ip.user_ns['tmp'], 0)
87 res = ip.run_cell('tmp=1;"""a\nb"""\n')
87 res = ip.run_cell('tmp=1;"""a\nb"""\n')
88 self.assertEqual(ip.user_ns['tmp'], 1)
88 self.assertEqual(ip.user_ns['tmp'], 1)
89 self.assertEqual(res.success, True)
89 self.assertEqual(res.success, True)
90 self.assertEqual(res.result, "a\nb")
90 self.assertEqual(res.result, "a\nb")
91
91
92 def test_dont_cache_with_semicolon(self):
92 def test_dont_cache_with_semicolon(self):
93 "Ending a line with semicolon should not cache the returned object (GH-307)"
93 "Ending a line with semicolon should not cache the returned object (GH-307)"
94 oldlen = len(ip.user_ns['Out'])
94 oldlen = len(ip.user_ns['Out'])
95 for cell in ['1;', '1;1;']:
95 for cell in ['1;', '1;1;']:
96 res = ip.run_cell(cell, store_history=True)
96 res = ip.run_cell(cell, store_history=True)
97 newlen = len(ip.user_ns['Out'])
97 newlen = len(ip.user_ns['Out'])
98 self.assertEqual(oldlen, newlen)
98 self.assertEqual(oldlen, newlen)
99 self.assertIsNone(res.result)
99 self.assertIsNone(res.result)
100 i = 0
100 i = 0
101 #also test the default caching behavior
101 #also test the default caching behavior
102 for cell in ['1', '1;1']:
102 for cell in ['1', '1;1']:
103 ip.run_cell(cell, store_history=True)
103 ip.run_cell(cell, store_history=True)
104 newlen = len(ip.user_ns['Out'])
104 newlen = len(ip.user_ns['Out'])
105 i += 1
105 i += 1
106 self.assertEqual(oldlen+i, newlen)
106 self.assertEqual(oldlen+i, newlen)
107
107
108 def test_syntax_error(self):
108 def test_syntax_error(self):
109 res = ip.run_cell("raise = 3")
109 res = ip.run_cell("raise = 3")
110 self.assertIsInstance(res.error_before_exec, SyntaxError)
110 self.assertIsInstance(res.error_before_exec, SyntaxError)
111
111
112 def test_open_standard_input_stream(self):
112 def test_open_standard_input_stream(self):
113 res = ip.run_cell("open(0)")
113 res = ip.run_cell("open(0)")
114 self.assertIsInstance(res.error_in_exec, ValueError)
114 self.assertIsInstance(res.error_in_exec, ValueError)
115
115
116 def test_open_standard_output_stream(self):
116 def test_open_standard_output_stream(self):
117 res = ip.run_cell("open(1)")
117 res = ip.run_cell("open(1)")
118 self.assertIsInstance(res.error_in_exec, ValueError)
118 self.assertIsInstance(res.error_in_exec, ValueError)
119
119
120 def test_open_standard_error_stream(self):
120 def test_open_standard_error_stream(self):
121 res = ip.run_cell("open(2)")
121 res = ip.run_cell("open(2)")
122 self.assertIsInstance(res.error_in_exec, ValueError)
122 self.assertIsInstance(res.error_in_exec, ValueError)
123
123
124 def test_In_variable(self):
124 def test_In_variable(self):
125 "Verify that In variable grows with user input (GH-284)"
125 "Verify that In variable grows with user input (GH-284)"
126 oldlen = len(ip.user_ns['In'])
126 oldlen = len(ip.user_ns['In'])
127 ip.run_cell('1;', store_history=True)
127 ip.run_cell('1;', store_history=True)
128 newlen = len(ip.user_ns['In'])
128 newlen = len(ip.user_ns['In'])
129 self.assertEqual(oldlen+1, newlen)
129 self.assertEqual(oldlen+1, newlen)
130 self.assertEqual(ip.user_ns['In'][-1],'1;')
130 self.assertEqual(ip.user_ns['In'][-1],'1;')
131
131
132 def test_magic_names_in_string(self):
132 def test_magic_names_in_string(self):
133 ip.run_cell('a = """\n%exit\n"""')
133 ip.run_cell('a = """\n%exit\n"""')
134 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
134 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
135
135
136 def test_trailing_newline(self):
136 def test_trailing_newline(self):
137 """test that running !(command) does not raise a SyntaxError"""
137 """test that running !(command) does not raise a SyntaxError"""
138 ip.run_cell('!(true)\n', False)
138 ip.run_cell('!(true)\n', False)
139 ip.run_cell('!(true)\n\n\n', False)
139 ip.run_cell('!(true)\n\n\n', False)
140
140
141 def test_gh_597(self):
141 def test_gh_597(self):
142 """Pretty-printing lists of objects with non-ascii reprs may cause
142 """Pretty-printing lists of objects with non-ascii reprs may cause
143 problems."""
143 problems."""
144 class Spam(object):
144 class Spam(object):
145 def __repr__(self):
145 def __repr__(self):
146 return "\xe9"*50
146 return "\xe9"*50
147 import IPython.core.formatters
147 import IPython.core.formatters
148 f = IPython.core.formatters.PlainTextFormatter()
148 f = IPython.core.formatters.PlainTextFormatter()
149 f([Spam(), Spam()])
149 f([Spam(), Spam()])
150
150
151 def test_future_flags(self):
151 def test_future_flags(self):
152 """Check that future flags are used for parsing code (gh-777)"""
152 """Check that future flags are used for parsing code (gh-777)"""
153 ip.run_cell('from __future__ import barry_as_FLUFL')
153 ip.run_cell('from __future__ import barry_as_FLUFL')
154 try:
154 try:
155 ip.run_cell('prfunc_return_val = 1 <> 2')
155 ip.run_cell('prfunc_return_val = 1 <> 2')
156 assert 'prfunc_return_val' in ip.user_ns
156 assert 'prfunc_return_val' in ip.user_ns
157 finally:
157 finally:
158 # Reset compiler flags so we don't mess up other tests.
158 # Reset compiler flags so we don't mess up other tests.
159 ip.compile.reset_compiler_flags()
159 ip.compile.reset_compiler_flags()
160
160
161 def test_can_pickle(self):
161 def test_can_pickle(self):
162 "Can we pickle objects defined interactively (GH-29)"
162 "Can we pickle objects defined interactively (GH-29)"
163 ip = get_ipython()
163 ip = get_ipython()
164 ip.reset()
164 ip.reset()
165 ip.run_cell(("class Mylist(list):\n"
165 ip.run_cell(("class Mylist(list):\n"
166 " def __init__(self,x=[]):\n"
166 " def __init__(self,x=[]):\n"
167 " list.__init__(self,x)"))
167 " list.__init__(self,x)"))
168 ip.run_cell("w=Mylist([1,2,3])")
168 ip.run_cell("w=Mylist([1,2,3])")
169
169
170 from pickle import dumps
170 from pickle import dumps
171
171
172 # We need to swap in our main module - this is only necessary
172 # We need to swap in our main module - this is only necessary
173 # inside the test framework, because IPython puts the interactive module
173 # inside the test framework, because IPython puts the interactive module
174 # in place (but the test framework undoes this).
174 # in place (but the test framework undoes this).
175 _main = sys.modules['__main__']
175 _main = sys.modules['__main__']
176 sys.modules['__main__'] = ip.user_module
176 sys.modules['__main__'] = ip.user_module
177 try:
177 try:
178 res = dumps(ip.user_ns["w"])
178 res = dumps(ip.user_ns["w"])
179 finally:
179 finally:
180 sys.modules['__main__'] = _main
180 sys.modules['__main__'] = _main
181 self.assertTrue(isinstance(res, bytes))
181 self.assertTrue(isinstance(res, bytes))
182
182
183 def test_global_ns(self):
183 def test_global_ns(self):
184 "Code in functions must be able to access variables outside them."
184 "Code in functions must be able to access variables outside them."
185 ip = get_ipython()
185 ip = get_ipython()
186 ip.run_cell("a = 10")
186 ip.run_cell("a = 10")
187 ip.run_cell(("def f(x):\n"
187 ip.run_cell(("def f(x):\n"
188 " return x + a"))
188 " return x + a"))
189 ip.run_cell("b = f(12)")
189 ip.run_cell("b = f(12)")
190 self.assertEqual(ip.user_ns["b"], 22)
190 self.assertEqual(ip.user_ns["b"], 22)
191
191
192 def test_bad_custom_tb(self):
192 def test_bad_custom_tb(self):
193 """Check that InteractiveShell is protected from bad custom exception handlers"""
193 """Check that InteractiveShell is protected from bad custom exception handlers"""
194 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
194 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
195 self.assertEqual(ip.custom_exceptions, (IOError,))
195 self.assertEqual(ip.custom_exceptions, (IOError,))
196 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
196 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
197 ip.run_cell(u'raise IOError("foo")')
197 ip.run_cell(u'raise IOError("foo")')
198 self.assertEqual(ip.custom_exceptions, ())
198 self.assertEqual(ip.custom_exceptions, ())
199
199
200 def test_bad_custom_tb_return(self):
200 def test_bad_custom_tb_return(self):
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
202 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
203 self.assertEqual(ip.custom_exceptions, (NameError,))
203 self.assertEqual(ip.custom_exceptions, (NameError,))
204 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
204 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
205 ip.run_cell(u'a=abracadabra')
205 ip.run_cell(u'a=abracadabra')
206 self.assertEqual(ip.custom_exceptions, ())
206 self.assertEqual(ip.custom_exceptions, ())
207
207
208 def test_drop_by_id(self):
208 def test_drop_by_id(self):
209 myvars = {"a":object(), "b":object(), "c": object()}
209 myvars = {"a":object(), "b":object(), "c": object()}
210 ip.push(myvars, interactive=False)
210 ip.push(myvars, interactive=False)
211 for name in myvars:
211 for name in myvars:
212 assert name in ip.user_ns, name
212 assert name in ip.user_ns, name
213 assert name in ip.user_ns_hidden, name
213 assert name in ip.user_ns_hidden, name
214 ip.user_ns['b'] = 12
214 ip.user_ns['b'] = 12
215 ip.drop_by_id(myvars)
215 ip.drop_by_id(myvars)
216 for name in ["a", "c"]:
216 for name in ["a", "c"]:
217 assert name not in ip.user_ns, name
217 assert name not in ip.user_ns, name
218 assert name not in ip.user_ns_hidden, name
218 assert name not in ip.user_ns_hidden, name
219 assert ip.user_ns['b'] == 12
219 assert ip.user_ns['b'] == 12
220 ip.reset()
220 ip.reset()
221
221
222 def test_var_expand(self):
222 def test_var_expand(self):
223 ip.user_ns['f'] = u'Ca\xf1o'
223 ip.user_ns['f'] = u'Ca\xf1o'
224 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
224 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
225 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
225 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
226 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
226 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
227 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
227 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
228
228
229 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
229 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
230
230
231 ip.user_ns['f'] = b'Ca\xc3\xb1o'
231 ip.user_ns['f'] = b'Ca\xc3\xb1o'
232 # This should not raise any exception:
232 # This should not raise any exception:
233 ip.var_expand(u'echo $f')
233 ip.var_expand(u'echo $f')
234
234
235 def test_var_expand_local(self):
235 def test_var_expand_local(self):
236 """Test local variable expansion in !system and %magic calls"""
236 """Test local variable expansion in !system and %magic calls"""
237 # !system
237 # !system
238 ip.run_cell(
238 ip.run_cell(
239 "def test():\n"
239 "def test():\n"
240 ' lvar = "ttt"\n'
240 ' lvar = "ttt"\n'
241 " ret = !echo {lvar}\n"
241 " ret = !echo {lvar}\n"
242 " return ret[0]\n"
242 " return ret[0]\n"
243 )
243 )
244 res = ip.user_ns["test"]()
244 res = ip.user_ns["test"]()
245 self.assertIn("ttt", res)
245 self.assertIn("ttt", res)
246
246
247 # %magic
247 # %magic
248 ip.run_cell(
248 ip.run_cell(
249 "def makemacro():\n"
249 "def makemacro():\n"
250 ' macroname = "macro_var_expand_locals"\n'
250 ' macroname = "macro_var_expand_locals"\n'
251 " %macro {macroname} codestr\n"
251 " %macro {macroname} codestr\n"
252 )
252 )
253 ip.user_ns["codestr"] = "str(12)"
253 ip.user_ns["codestr"] = "str(12)"
254 ip.run_cell("makemacro()")
254 ip.run_cell("makemacro()")
255 self.assertIn("macro_var_expand_locals", ip.user_ns)
255 self.assertIn("macro_var_expand_locals", ip.user_ns)
256
256
257 def test_var_expand_self(self):
257 def test_var_expand_self(self):
258 """Test variable expansion with the name 'self', which was failing.
258 """Test variable expansion with the name 'self', which was failing.
259
259
260 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
260 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
261 """
261 """
262 ip.run_cell(
262 ip.run_cell(
263 "class cTest:\n"
263 "class cTest:\n"
264 ' classvar="see me"\n'
264 ' classvar="see me"\n'
265 " def test(self):\n"
265 " def test(self):\n"
266 " res = !echo Variable: {self.classvar}\n"
266 " res = !echo Variable: {self.classvar}\n"
267 " return res[0]\n"
267 " return res[0]\n"
268 )
268 )
269 self.assertIn("see me", ip.user_ns["cTest"]().test())
269 self.assertIn("see me", ip.user_ns["cTest"]().test())
270
270
271 def test_bad_var_expand(self):
271 def test_bad_var_expand(self):
272 """var_expand on invalid formats shouldn't raise"""
272 """var_expand on invalid formats shouldn't raise"""
273 # SyntaxError
273 # SyntaxError
274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
275 # NameError
275 # NameError
276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
277 # ZeroDivisionError
277 # ZeroDivisionError
278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
279
279
280 def test_silent_postexec(self):
280 def test_silent_postexec(self):
281 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
281 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
282 pre_explicit = mock.Mock()
282 pre_explicit = mock.Mock()
283 pre_always = mock.Mock()
283 pre_always = mock.Mock()
284 post_explicit = mock.Mock()
284 post_explicit = mock.Mock()
285 post_always = mock.Mock()
285 post_always = mock.Mock()
286 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
286 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
287
287
288 ip.events.register('pre_run_cell', pre_explicit)
288 ip.events.register('pre_run_cell', pre_explicit)
289 ip.events.register('pre_execute', pre_always)
289 ip.events.register('pre_execute', pre_always)
290 ip.events.register('post_run_cell', post_explicit)
290 ip.events.register('post_run_cell', post_explicit)
291 ip.events.register('post_execute', post_always)
291 ip.events.register('post_execute', post_always)
292
292
293 try:
293 try:
294 ip.run_cell("1", silent=True)
294 ip.run_cell("1", silent=True)
295 assert pre_always.called
295 assert pre_always.called
296 assert not pre_explicit.called
296 assert not pre_explicit.called
297 assert post_always.called
297 assert post_always.called
298 assert not post_explicit.called
298 assert not post_explicit.called
299 # double-check that non-silent exec did what we expected
299 # double-check that non-silent exec did what we expected
300 # silent to avoid
300 # silent to avoid
301 ip.run_cell("1")
301 ip.run_cell("1")
302 assert pre_explicit.called
302 assert pre_explicit.called
303 assert post_explicit.called
303 assert post_explicit.called
304 info, = pre_explicit.call_args[0]
304 info, = pre_explicit.call_args[0]
305 result, = post_explicit.call_args[0]
305 result, = post_explicit.call_args[0]
306 self.assertEqual(info, result.info)
306 self.assertEqual(info, result.info)
307 # check that post hooks are always called
307 # check that post hooks are always called
308 [m.reset_mock() for m in all_mocks]
308 [m.reset_mock() for m in all_mocks]
309 ip.run_cell("syntax error")
309 ip.run_cell("syntax error")
310 assert pre_always.called
310 assert pre_always.called
311 assert pre_explicit.called
311 assert pre_explicit.called
312 assert post_always.called
312 assert post_always.called
313 assert post_explicit.called
313 assert post_explicit.called
314 info, = pre_explicit.call_args[0]
314 info, = pre_explicit.call_args[0]
315 result, = post_explicit.call_args[0]
315 result, = post_explicit.call_args[0]
316 self.assertEqual(info, result.info)
316 self.assertEqual(info, result.info)
317 finally:
317 finally:
318 # remove post-exec
318 # remove post-exec
319 ip.events.unregister('pre_run_cell', pre_explicit)
319 ip.events.unregister('pre_run_cell', pre_explicit)
320 ip.events.unregister('pre_execute', pre_always)
320 ip.events.unregister('pre_execute', pre_always)
321 ip.events.unregister('post_run_cell', post_explicit)
321 ip.events.unregister('post_run_cell', post_explicit)
322 ip.events.unregister('post_execute', post_always)
322 ip.events.unregister('post_execute', post_always)
323
323
324 def test_silent_noadvance(self):
324 def test_silent_noadvance(self):
325 """run_cell(silent=True) doesn't advance execution_count"""
325 """run_cell(silent=True) doesn't advance execution_count"""
326 ec = ip.execution_count
326 ec = ip.execution_count
327 # silent should force store_history=False
327 # silent should force store_history=False
328 ip.run_cell("1", store_history=True, silent=True)
328 ip.run_cell("1", store_history=True, silent=True)
329
329
330 self.assertEqual(ec, ip.execution_count)
330 self.assertEqual(ec, ip.execution_count)
331 # double-check that non-silent exec did what we expected
331 # double-check that non-silent exec did what we expected
332 # silent to avoid
332 # silent to avoid
333 ip.run_cell("1", store_history=True)
333 ip.run_cell("1", store_history=True)
334 self.assertEqual(ec+1, ip.execution_count)
334 self.assertEqual(ec+1, ip.execution_count)
335
335
336 def test_silent_nodisplayhook(self):
336 def test_silent_nodisplayhook(self):
337 """run_cell(silent=True) doesn't trigger displayhook"""
337 """run_cell(silent=True) doesn't trigger displayhook"""
338 d = dict(called=False)
338 d = dict(called=False)
339
339
340 trap = ip.display_trap
340 trap = ip.display_trap
341 save_hook = trap.hook
341 save_hook = trap.hook
342
342
343 def failing_hook(*args, **kwargs):
343 def failing_hook(*args, **kwargs):
344 d['called'] = True
344 d['called'] = True
345
345
346 try:
346 try:
347 trap.hook = failing_hook
347 trap.hook = failing_hook
348 res = ip.run_cell("1", silent=True)
348 res = ip.run_cell("1", silent=True)
349 self.assertFalse(d['called'])
349 self.assertFalse(d['called'])
350 self.assertIsNone(res.result)
350 self.assertIsNone(res.result)
351 # double-check that non-silent exec did what we expected
351 # double-check that non-silent exec did what we expected
352 # silent to avoid
352 # silent to avoid
353 ip.run_cell("1")
353 ip.run_cell("1")
354 self.assertTrue(d['called'])
354 self.assertTrue(d['called'])
355 finally:
355 finally:
356 trap.hook = save_hook
356 trap.hook = save_hook
357
357
358 def test_ofind_line_magic(self):
358 def test_ofind_line_magic(self):
359 from IPython.core.magic import register_line_magic
359 from IPython.core.magic import register_line_magic
360
360
361 @register_line_magic
361 @register_line_magic
362 def lmagic(line):
362 def lmagic(line):
363 "A line magic"
363 "A line magic"
364
364
365 # Get info on line magic
365 # Get info on line magic
366 lfind = ip._ofind("lmagic")
366 lfind = ip._ofind("lmagic")
367 info = OInfo(
367 info = OInfo(
368 found=True,
368 found=True,
369 isalias=False,
369 isalias=False,
370 ismagic=True,
370 ismagic=True,
371 namespace="IPython internal",
371 namespace="IPython internal",
372 obj=lmagic,
372 obj=lmagic,
373 parent=None,
373 parent=None,
374 )
374 )
375 self.assertEqual(lfind, info)
375 self.assertEqual(lfind, info)
376
376
377 def test_ofind_cell_magic(self):
377 def test_ofind_cell_magic(self):
378 from IPython.core.magic import register_cell_magic
378 from IPython.core.magic import register_cell_magic
379
379
380 @register_cell_magic
380 @register_cell_magic
381 def cmagic(line, cell):
381 def cmagic(line, cell):
382 "A cell magic"
382 "A cell magic"
383
383
384 # Get info on cell magic
384 # Get info on cell magic
385 find = ip._ofind("cmagic")
385 find = ip._ofind("cmagic")
386 info = OInfo(
386 info = OInfo(
387 found=True,
387 found=True,
388 isalias=False,
388 isalias=False,
389 ismagic=True,
389 ismagic=True,
390 namespace="IPython internal",
390 namespace="IPython internal",
391 obj=cmagic,
391 obj=cmagic,
392 parent=None,
392 parent=None,
393 )
393 )
394 self.assertEqual(find, info)
394 self.assertEqual(find, info)
395
395
396 def test_ofind_property_with_error(self):
396 def test_ofind_property_with_error(self):
397 class A(object):
397 class A(object):
398 @property
398 @property
399 def foo(self):
399 def foo(self):
400 raise NotImplementedError() # pragma: no cover
400 raise NotImplementedError() # pragma: no cover
401
401
402 a = A()
402 a = A()
403
403
404 found = ip._ofind("a.foo", [("locals", locals())])
404 found = ip._ofind("a.foo", [("locals", locals())])
405 info = OInfo(
405 info = OInfo(
406 found=True,
406 found=True,
407 isalias=False,
407 isalias=False,
408 ismagic=False,
408 ismagic=False,
409 namespace="locals",
409 namespace="locals",
410 obj=A.foo,
410 obj=A.foo,
411 parent=a,
411 parent=a,
412 )
412 )
413 self.assertEqual(found, info)
413 self.assertEqual(found, info)
414
414
415 def test_ofind_multiple_attribute_lookups(self):
415 def test_ofind_multiple_attribute_lookups(self):
416 class A(object):
416 class A(object):
417 @property
417 @property
418 def foo(self):
418 def foo(self):
419 raise NotImplementedError() # pragma: no cover
419 raise NotImplementedError() # pragma: no cover
420
420
421 a = A()
421 a = A()
422 a.a = A()
422 a.a = A()
423 a.a.a = A()
423 a.a.a = A()
424
424
425 found = ip._ofind("a.a.a.foo", [("locals", locals())])
425 found = ip._ofind("a.a.a.foo", [("locals", locals())])
426 info = OInfo(
426 info = OInfo(
427 found=True,
427 found=True,
428 isalias=False,
428 isalias=False,
429 ismagic=False,
429 ismagic=False,
430 namespace="locals",
430 namespace="locals",
431 obj=A.foo,
431 obj=A.foo,
432 parent=a.a.a,
432 parent=a.a.a,
433 )
433 )
434 self.assertEqual(found, info)
434 self.assertEqual(found, info)
435
435
436 def test_ofind_slotted_attributes(self):
436 def test_ofind_slotted_attributes(self):
437 class A(object):
437 class A(object):
438 __slots__ = ['foo']
438 __slots__ = ['foo']
439 def __init__(self):
439 def __init__(self):
440 self.foo = 'bar'
440 self.foo = 'bar'
441
441
442 a = A()
442 a = A()
443 found = ip._ofind("a.foo", [("locals", locals())])
443 found = ip._ofind("a.foo", [("locals", locals())])
444 info = OInfo(
444 info = OInfo(
445 found=True,
445 found=True,
446 isalias=False,
446 isalias=False,
447 ismagic=False,
447 ismagic=False,
448 namespace="locals",
448 namespace="locals",
449 obj=a.foo,
449 obj=a.foo,
450 parent=a,
450 parent=a,
451 )
451 )
452 self.assertEqual(found, info)
452 self.assertEqual(found, info)
453
453
454 found = ip._ofind("a.bar", [("locals", locals())])
454 found = ip._ofind("a.bar", [("locals", locals())])
455 expected = OInfo(
455 expected = OInfo(
456 found=False,
456 found=False,
457 isalias=False,
457 isalias=False,
458 ismagic=False,
458 ismagic=False,
459 namespace=None,
459 namespace=None,
460 obj=None,
460 obj=None,
461 parent=a,
461 parent=a,
462 )
462 )
463 assert found == expected
463 assert found == expected
464
464
465 def test_ofind_prefers_property_to_instance_level_attribute(self):
465 def test_ofind_prefers_property_to_instance_level_attribute(self):
466 class A(object):
466 class A(object):
467 @property
467 @property
468 def foo(self):
468 def foo(self):
469 return 'bar'
469 return 'bar'
470 a = A()
470 a = A()
471 a.__dict__["foo"] = "baz"
471 a.__dict__["foo"] = "baz"
472 self.assertEqual(a.foo, "bar")
472 self.assertEqual(a.foo, "bar")
473 found = ip._ofind("a.foo", [("locals", locals())])
473 found = ip._ofind("a.foo", [("locals", locals())])
474 self.assertIs(found.obj, A.foo)
474 self.assertIs(found.obj, A.foo)
475
475
476 def test_custom_syntaxerror_exception(self):
476 def test_custom_syntaxerror_exception(self):
477 called = []
477 called = []
478 def my_handler(shell, etype, value, tb, tb_offset=None):
478 def my_handler(shell, etype, value, tb, tb_offset=None):
479 called.append(etype)
479 called.append(etype)
480 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
480 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
481
481
482 ip.set_custom_exc((SyntaxError,), my_handler)
482 ip.set_custom_exc((SyntaxError,), my_handler)
483 try:
483 try:
484 ip.run_cell("1f")
484 ip.run_cell("1f")
485 # Check that this was called, and only once.
485 # Check that this was called, and only once.
486 self.assertEqual(called, [SyntaxError])
486 self.assertEqual(called, [SyntaxError])
487 finally:
487 finally:
488 # Reset the custom exception hook
488 # Reset the custom exception hook
489 ip.set_custom_exc((), None)
489 ip.set_custom_exc((), None)
490
490
491 def test_custom_exception(self):
491 def test_custom_exception(self):
492 called = []
492 called = []
493 def my_handler(shell, etype, value, tb, tb_offset=None):
493 def my_handler(shell, etype, value, tb, tb_offset=None):
494 called.append(etype)
494 called.append(etype)
495 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
495 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
496
496
497 ip.set_custom_exc((ValueError,), my_handler)
497 ip.set_custom_exc((ValueError,), my_handler)
498 try:
498 try:
499 res = ip.run_cell("raise ValueError('test')")
499 res = ip.run_cell("raise ValueError('test')")
500 # Check that this was called, and only once.
500 # Check that this was called, and only once.
501 self.assertEqual(called, [ValueError])
501 self.assertEqual(called, [ValueError])
502 # Check that the error is on the result object
502 # Check that the error is on the result object
503 self.assertIsInstance(res.error_in_exec, ValueError)
503 self.assertIsInstance(res.error_in_exec, ValueError)
504 finally:
504 finally:
505 # Reset the custom exception hook
505 # Reset the custom exception hook
506 ip.set_custom_exc((), None)
506 ip.set_custom_exc((), None)
507
507
508 @mock.patch("builtins.print")
508 @mock.patch("builtins.print")
509 def test_showtraceback_with_surrogates(self, mocked_print):
509 def test_showtraceback_with_surrogates(self, mocked_print):
510 values = []
510 values = []
511
511
512 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
512 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
513 values.append(value)
513 values.append(value)
514 if value == chr(0xD8FF):
514 if value == chr(0xD8FF):
515 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
515 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
516
516
517 # mock builtins.print
517 # mock builtins.print
518 mocked_print.side_effect = mock_print_func
518 mocked_print.side_effect = mock_print_func
519
519
520 # ip._showtraceback() is replaced in globalipapp.py.
520 # ip._showtraceback() is replaced in globalipapp.py.
521 # Call original method to test.
521 # Call original method to test.
522 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
522 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
523
523
524 self.assertEqual(mocked_print.call_count, 2)
524 self.assertEqual(mocked_print.call_count, 2)
525 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
525 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
526
526
527 def test_mktempfile(self):
527 def test_mktempfile(self):
528 filename = ip.mktempfile()
528 filename = ip.mktempfile()
529 # Check that we can open the file again on Windows
529 # Check that we can open the file again on Windows
530 with open(filename, "w", encoding="utf-8") as f:
530 with open(filename, "w", encoding="utf-8") as f:
531 f.write("abc")
531 f.write("abc")
532
532
533 filename = ip.mktempfile(data="blah")
533 filename = ip.mktempfile(data="blah")
534 with open(filename, "r", encoding="utf-8") as f:
534 with open(filename, "r", encoding="utf-8") as f:
535 self.assertEqual(f.read(), "blah")
535 self.assertEqual(f.read(), "blah")
536
536
537 def test_new_main_mod(self):
537 def test_new_main_mod(self):
538 # Smoketest to check that this accepts a unicode module name
538 # Smoketest to check that this accepts a unicode module name
539 name = u'jiefmw'
539 name = u'jiefmw'
540 mod = ip.new_main_mod(u'%s.py' % name, name)
540 mod = ip.new_main_mod(u'%s.py' % name, name)
541 self.assertEqual(mod.__name__, name)
541 self.assertEqual(mod.__name__, name)
542
542
543 def test_get_exception_only(self):
543 def test_get_exception_only(self):
544 try:
544 try:
545 raise KeyboardInterrupt
545 raise KeyboardInterrupt
546 except KeyboardInterrupt:
546 except KeyboardInterrupt:
547 msg = ip.get_exception_only()
547 msg = ip.get_exception_only()
548 self.assertEqual(msg, 'KeyboardInterrupt\n')
548 self.assertEqual(msg, 'KeyboardInterrupt\n')
549
549
550 try:
550 try:
551 raise DerivedInterrupt("foo")
551 raise DerivedInterrupt("foo")
552 except KeyboardInterrupt:
552 except KeyboardInterrupt:
553 msg = ip.get_exception_only()
553 msg = ip.get_exception_only()
554 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
554 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
555
555
556 def test_inspect_text(self):
556 def test_inspect_text(self):
557 ip.run_cell('a = 5')
557 ip.run_cell('a = 5')
558 text = ip.object_inspect_text('a')
558 text = ip.object_inspect_text('a')
559 self.assertIsInstance(text, str)
559 self.assertIsInstance(text, str)
560
560
561 def test_last_execution_result(self):
561 def test_last_execution_result(self):
562 """ Check that last execution result gets set correctly (GH-10702) """
562 """ Check that last execution result gets set correctly (GH-10702) """
563 result = ip.run_cell('a = 5; a')
563 result = ip.run_cell('a = 5; a')
564 self.assertTrue(ip.last_execution_succeeded)
564 self.assertTrue(ip.last_execution_succeeded)
565 self.assertEqual(ip.last_execution_result.result, 5)
565 self.assertEqual(ip.last_execution_result.result, 5)
566
566
567 result = ip.run_cell('a = x_invalid_id_x')
567 result = ip.run_cell('a = x_invalid_id_x')
568 self.assertFalse(ip.last_execution_succeeded)
568 self.assertFalse(ip.last_execution_succeeded)
569 self.assertFalse(ip.last_execution_result.success)
569 self.assertFalse(ip.last_execution_result.success)
570 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
570 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
571
571
572 def test_reset_aliasing(self):
572 def test_reset_aliasing(self):
573 """ Check that standard posix aliases work after %reset. """
573 """ Check that standard posix aliases work after %reset. """
574 if os.name != 'posix':
574 if os.name != 'posix':
575 return
575 return
576
576
577 ip.reset()
577 ip.reset()
578 for cmd in ('clear', 'more', 'less', 'man'):
578 for cmd in ('clear', 'more', 'less', 'man'):
579 res = ip.run_cell('%' + cmd)
579 res = ip.run_cell('%' + cmd)
580 self.assertEqual(res.success, True)
580 self.assertEqual(res.success, True)
581
581
582
582
583 @pytest.mark.skipif(
583 @pytest.mark.skipif(
584 sys.implementation.name == "pypy"
584 sys.implementation.name == "pypy"
585 and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
585 and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
586 reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
586 reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
587 )
587 )
588 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
588 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
589 @onlyif_unicode_paths
589 @onlyif_unicode_paths
590 def setUp(self):
590 def setUp(self):
591 self.BASETESTDIR = tempfile.mkdtemp()
591 self.BASETESTDIR = tempfile.mkdtemp()
592 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
592 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
593 os.mkdir(self.TESTDIR)
593 os.mkdir(self.TESTDIR)
594 with open(
594 with open(
595 join(self.TESTDIR, "Γ₯Àâtestscript.py"), "w", encoding="utf-8"
595 join(self.TESTDIR, "Γ₯Àâtestscript.py"), "w", encoding="utf-8"
596 ) as sfile:
596 ) as sfile:
597 sfile.write("pass\n")
597 sfile.write("pass\n")
598 self.oldpath = os.getcwd()
598 self.oldpath = os.getcwd()
599 os.chdir(self.TESTDIR)
599 os.chdir(self.TESTDIR)
600 self.fname = u"Γ₯Àâtestscript.py"
600 self.fname = u"Γ₯Àâtestscript.py"
601
601
602 def tearDown(self):
602 def tearDown(self):
603 os.chdir(self.oldpath)
603 os.chdir(self.oldpath)
604 shutil.rmtree(self.BASETESTDIR)
604 shutil.rmtree(self.BASETESTDIR)
605
605
606 @onlyif_unicode_paths
606 @onlyif_unicode_paths
607 def test_1(self):
607 def test_1(self):
608 """Test safe_execfile with non-ascii path
608 """Test safe_execfile with non-ascii path
609 """
609 """
610 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
610 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
611
611
612 class ExitCodeChecks(tt.TempFileMixin):
612 class ExitCodeChecks(tt.TempFileMixin):
613
613
614 def setUp(self):
614 def setUp(self):
615 self.system = ip.system_raw
615 self.system = ip.system_raw
616
616
617 def test_exit_code_ok(self):
617 def test_exit_code_ok(self):
618 self.system('exit 0')
618 self.system('exit 0')
619 self.assertEqual(ip.user_ns['_exit_code'], 0)
619 self.assertEqual(ip.user_ns['_exit_code'], 0)
620
620
621 def test_exit_code_error(self):
621 def test_exit_code_error(self):
622 self.system('exit 1')
622 self.system('exit 1')
623 self.assertEqual(ip.user_ns['_exit_code'], 1)
623 self.assertEqual(ip.user_ns['_exit_code'], 1)
624
624
625 @skipif(not hasattr(signal, 'SIGALRM'))
625 @skipif(not hasattr(signal, 'SIGALRM'))
626 def test_exit_code_signal(self):
626 def test_exit_code_signal(self):
627 self.mktmp("import signal, time\n"
627 self.mktmp("import signal, time\n"
628 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
628 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
629 "time.sleep(1)\n")
629 "time.sleep(1)\n")
630 self.system("%s %s" % (sys.executable, self.fname))
630 self.system("%s %s" % (sys.executable, self.fname))
631 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
631 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
632
632
633 @onlyif_cmds_exist("csh")
633 @onlyif_cmds_exist("csh")
634 def test_exit_code_signal_csh(self): # pragma: no cover
634 def test_exit_code_signal_csh(self): # pragma: no cover
635 SHELL = os.environ.get("SHELL", None)
635 SHELL = os.environ.get("SHELL", None)
636 os.environ["SHELL"] = find_cmd("csh")
636 os.environ["SHELL"] = find_cmd("csh")
637 try:
637 try:
638 self.test_exit_code_signal()
638 self.test_exit_code_signal()
639 finally:
639 finally:
640 if SHELL is not None:
640 if SHELL is not None:
641 os.environ['SHELL'] = SHELL
641 os.environ['SHELL'] = SHELL
642 else:
642 else:
643 del os.environ['SHELL']
643 del os.environ['SHELL']
644
644
645
645
646 class TestSystemRaw(ExitCodeChecks):
646 class TestSystemRaw(ExitCodeChecks):
647
647
648 def setUp(self):
648 def setUp(self):
649 super().setUp()
649 super().setUp()
650 self.system = ip.system_raw
650 self.system = ip.system_raw
651
651
652 @onlyif_unicode_paths
652 @onlyif_unicode_paths
653 def test_1(self):
653 def test_1(self):
654 """Test system_raw with non-ascii cmd
654 """Test system_raw with non-ascii cmd
655 """
655 """
656 cmd = u'''python -c "'Γ₯Àâ'" '''
656 cmd = u'''python -c "'Γ₯Àâ'" '''
657 ip.system_raw(cmd)
657 ip.system_raw(cmd)
658
658
659 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
659 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
660 @mock.patch('os.system', side_effect=KeyboardInterrupt)
660 @mock.patch('os.system', side_effect=KeyboardInterrupt)
661 def test_control_c(self, *mocks):
661 def test_control_c(self, *mocks):
662 try:
662 try:
663 self.system("sleep 1 # won't happen")
663 self.system("sleep 1 # won't happen")
664 except KeyboardInterrupt: # pragma: no cove
664 except KeyboardInterrupt: # pragma: no cove
665 self.fail(
665 self.fail(
666 "system call should intercept "
666 "system call should intercept "
667 "keyboard interrupt from subprocess.call"
667 "keyboard interrupt from subprocess.call"
668 )
668 )
669 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
669 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
670
670
671
671
672 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
672 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
673 def test_magic_warnings(magic_cmd):
673 def test_magic_warnings(magic_cmd):
674 if sys.platform == "win32":
674 if sys.platform == "win32":
675 to_mock = "os.system"
675 to_mock = "os.system"
676 expected_arg, expected_kwargs = magic_cmd, dict()
676 expected_arg, expected_kwargs = magic_cmd, dict()
677 else:
677 else:
678 to_mock = "subprocess.call"
678 to_mock = "subprocess.call"
679 expected_arg, expected_kwargs = magic_cmd, dict(
679 expected_arg, expected_kwargs = magic_cmd, dict(
680 shell=True, executable=os.environ.get("SHELL", None)
680 shell=True, executable=os.environ.get("SHELL", None)
681 )
681 )
682
682
683 with mock.patch(to_mock, return_value=0) as mock_sub:
683 with mock.patch(to_mock, return_value=0) as mock_sub:
684 with pytest.warns(Warning, match=r"You executed the system command"):
684 with pytest.warns(Warning, match=r"You executed the system command"):
685 ip.system_raw(magic_cmd)
685 ip.system_raw(magic_cmd)
686 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
686 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
687
687
688
688
689 # TODO: Exit codes are currently ignored on Windows.
689 # TODO: Exit codes are currently ignored on Windows.
690 class TestSystemPipedExitCode(ExitCodeChecks):
690 class TestSystemPipedExitCode(ExitCodeChecks):
691
691
692 def setUp(self):
692 def setUp(self):
693 super().setUp()
693 super().setUp()
694 self.system = ip.system_piped
694 self.system = ip.system_piped
695
695
696 @skip_win32
696 @skip_win32
697 def test_exit_code_ok(self):
697 def test_exit_code_ok(self):
698 ExitCodeChecks.test_exit_code_ok(self)
698 ExitCodeChecks.test_exit_code_ok(self)
699
699
700 @skip_win32
700 @skip_win32
701 def test_exit_code_error(self):
701 def test_exit_code_error(self):
702 ExitCodeChecks.test_exit_code_error(self)
702 ExitCodeChecks.test_exit_code_error(self)
703
703
704 @skip_win32
704 @skip_win32
705 def test_exit_code_signal(self):
705 def test_exit_code_signal(self):
706 ExitCodeChecks.test_exit_code_signal(self)
706 ExitCodeChecks.test_exit_code_signal(self)
707
707
708 class TestModules(tt.TempFileMixin):
708 class TestModules(tt.TempFileMixin):
709 def test_extraneous_loads(self):
709 def test_extraneous_loads(self):
710 """Test we're not loading modules on startup that we shouldn't.
710 """Test we're not loading modules on startup that we shouldn't.
711 """
711 """
712 self.mktmp("import sys\n"
712 self.mktmp("import sys\n"
713 "print('numpy' in sys.modules)\n"
713 "print('numpy' in sys.modules)\n"
714 "print('ipyparallel' in sys.modules)\n"
714 "print('ipyparallel' in sys.modules)\n"
715 "print('ipykernel' in sys.modules)\n"
715 "print('ipykernel' in sys.modules)\n"
716 )
716 )
717 out = "False\nFalse\nFalse\n"
717 out = "False\nFalse\nFalse\n"
718 tt.ipexec_validate(self.fname, out)
718 tt.ipexec_validate(self.fname, out)
719
719
720 class Negator(ast.NodeTransformer):
720 class Negator(ast.NodeTransformer):
721 """Negates all number literals in an AST."""
721 """Negates all number literals in an AST."""
722
722
723 def visit_Num(self, node):
723 def visit_Num(self, node):
724 node.n = -node.n
724 node.value = -node.value
725 return node
725 return node
726
726
727 def visit_Constant(self, node):
727 def visit_Constant(self, node):
728 if isinstance(node.value, int):
728 if isinstance(node.value, int):
729 return self.visit_Num(node)
729 return self.visit_Num(node)
730 return node
730 return node
731
731
732 class TestAstTransform(unittest.TestCase):
732 class TestAstTransform(unittest.TestCase):
733 def setUp(self):
733 def setUp(self):
734 self.negator = Negator()
734 self.negator = Negator()
735 ip.ast_transformers.append(self.negator)
735 ip.ast_transformers.append(self.negator)
736
736
737 def tearDown(self):
737 def tearDown(self):
738 ip.ast_transformers.remove(self.negator)
738 ip.ast_transformers.remove(self.negator)
739
739
740 def test_non_int_const(self):
740 def test_non_int_const(self):
741 with tt.AssertPrints("hello"):
741 with tt.AssertPrints("hello"):
742 ip.run_cell('print("hello")')
742 ip.run_cell('print("hello")')
743
743
744 def test_run_cell(self):
744 def test_run_cell(self):
745 with tt.AssertPrints("-34"):
745 with tt.AssertPrints("-34"):
746 ip.run_cell("print(12 + 22)")
746 ip.run_cell("print(12 + 22)")
747
747
748 # A named reference to a number shouldn't be transformed.
748 # A named reference to a number shouldn't be transformed.
749 ip.user_ns["n"] = 55
749 ip.user_ns["n"] = 55
750 with tt.AssertNotPrints("-55"):
750 with tt.AssertNotPrints("-55"):
751 ip.run_cell("print(n)")
751 ip.run_cell("print(n)")
752
752
753 def test_timeit(self):
753 def test_timeit(self):
754 called = set()
754 called = set()
755 def f(x):
755 def f(x):
756 called.add(x)
756 called.add(x)
757 ip.push({'f':f})
757 ip.push({'f':f})
758
758
759 with tt.AssertPrints("std. dev. of"):
759 with tt.AssertPrints("std. dev. of"):
760 ip.run_line_magic("timeit", "-n1 f(1)")
760 ip.run_line_magic("timeit", "-n1 f(1)")
761 self.assertEqual(called, {-1})
761 self.assertEqual(called, {-1})
762 called.clear()
762 called.clear()
763
763
764 with tt.AssertPrints("std. dev. of"):
764 with tt.AssertPrints("std. dev. of"):
765 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
765 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
766 self.assertEqual(called, {-2, -3})
766 self.assertEqual(called, {-2, -3})
767
767
768 def test_time(self):
768 def test_time(self):
769 called = []
769 called = []
770 def f(x):
770 def f(x):
771 called.append(x)
771 called.append(x)
772 ip.push({'f':f})
772 ip.push({'f':f})
773
773
774 # Test with an expression
774 # Test with an expression
775 with tt.AssertPrints("Wall time: "):
775 with tt.AssertPrints("Wall time: "):
776 ip.run_line_magic("time", "f(5+9)")
776 ip.run_line_magic("time", "f(5+9)")
777 self.assertEqual(called, [-14])
777 self.assertEqual(called, [-14])
778 called[:] = []
778 called[:] = []
779
779
780 # Test with a statement (different code path)
780 # Test with a statement (different code path)
781 with tt.AssertPrints("Wall time: "):
781 with tt.AssertPrints("Wall time: "):
782 ip.run_line_magic("time", "a = f(-3 + -2)")
782 ip.run_line_magic("time", "a = f(-3 + -2)")
783 self.assertEqual(called, [5])
783 self.assertEqual(called, [5])
784
784
785 def test_macro(self):
785 def test_macro(self):
786 ip.push({'a':10})
786 ip.push({'a':10})
787 # The AST transformation makes this do a+=-1
787 # The AST transformation makes this do a+=-1
788 ip.define_macro("amacro", "a+=1\nprint(a)")
788 ip.define_macro("amacro", "a+=1\nprint(a)")
789
789
790 with tt.AssertPrints("9"):
790 with tt.AssertPrints("9"):
791 ip.run_cell("amacro")
791 ip.run_cell("amacro")
792 with tt.AssertPrints("8"):
792 with tt.AssertPrints("8"):
793 ip.run_cell("amacro")
793 ip.run_cell("amacro")
794
794
795 class TestMiscTransform(unittest.TestCase):
795 class TestMiscTransform(unittest.TestCase):
796
796
797
797
798 def test_transform_only_once(self):
798 def test_transform_only_once(self):
799 cleanup = 0
799 cleanup = 0
800 line_t = 0
800 line_t = 0
801 def count_cleanup(lines):
801 def count_cleanup(lines):
802 nonlocal cleanup
802 nonlocal cleanup
803 cleanup += 1
803 cleanup += 1
804 return lines
804 return lines
805
805
806 def count_line_t(lines):
806 def count_line_t(lines):
807 nonlocal line_t
807 nonlocal line_t
808 line_t += 1
808 line_t += 1
809 return lines
809 return lines
810
810
811 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
811 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
812 ip.input_transformer_manager.line_transforms.append(count_line_t)
812 ip.input_transformer_manager.line_transforms.append(count_line_t)
813
813
814 ip.run_cell('1')
814 ip.run_cell('1')
815
815
816 assert cleanup == 1
816 assert cleanup == 1
817 assert line_t == 1
817 assert line_t == 1
818
818
819 class IntegerWrapper(ast.NodeTransformer):
819 class IntegerWrapper(ast.NodeTransformer):
820 """Wraps all integers in a call to Integer()"""
820 """Wraps all integers in a call to Integer()"""
821
821
822 # for Python 3.7 and earlier
822 # for Python 3.7 and earlier
823
823
824 # for Python 3.7 and earlier
824 # for Python 3.7 and earlier
825 def visit_Num(self, node):
825 def visit_Num(self, node):
826 if isinstance(node.n, int):
826 if isinstance(node.n, int):
827 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
827 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
828 args=[node], keywords=[])
828 args=[node], keywords=[])
829 return node
829 return node
830
830
831 # For Python 3.8+
831 # For Python 3.8+
832 def visit_Constant(self, node):
832 def visit_Constant(self, node):
833 if isinstance(node.value, int):
833 if isinstance(node.value, int):
834 return self.visit_Num(node)
834 return self.visit_Num(node)
835 return node
835 return node
836
836
837
837
838 class TestAstTransform2(unittest.TestCase):
838 class TestAstTransform2(unittest.TestCase):
839 def setUp(self):
839 def setUp(self):
840 self.intwrapper = IntegerWrapper()
840 self.intwrapper = IntegerWrapper()
841 ip.ast_transformers.append(self.intwrapper)
841 ip.ast_transformers.append(self.intwrapper)
842
842
843 self.calls = []
843 self.calls = []
844 def Integer(*args):
844 def Integer(*args):
845 self.calls.append(args)
845 self.calls.append(args)
846 return args
846 return args
847 ip.push({"Integer": Integer})
847 ip.push({"Integer": Integer})
848
848
849 def tearDown(self):
849 def tearDown(self):
850 ip.ast_transformers.remove(self.intwrapper)
850 ip.ast_transformers.remove(self.intwrapper)
851 del ip.user_ns['Integer']
851 del ip.user_ns['Integer']
852
852
853 def test_run_cell(self):
853 def test_run_cell(self):
854 ip.run_cell("n = 2")
854 ip.run_cell("n = 2")
855 self.assertEqual(self.calls, [(2,)])
855 self.assertEqual(self.calls, [(2,)])
856
856
857 # This shouldn't throw an error
857 # This shouldn't throw an error
858 ip.run_cell("o = 2.0")
858 ip.run_cell("o = 2.0")
859 self.assertEqual(ip.user_ns['o'], 2.0)
859 self.assertEqual(ip.user_ns['o'], 2.0)
860
860
861 def test_run_cell_non_int(self):
861 def test_run_cell_non_int(self):
862 ip.run_cell("n = 'a'")
862 ip.run_cell("n = 'a'")
863 assert self.calls == []
863 assert self.calls == []
864
864
865 def test_timeit(self):
865 def test_timeit(self):
866 called = set()
866 called = set()
867 def f(x):
867 def f(x):
868 called.add(x)
868 called.add(x)
869 ip.push({'f':f})
869 ip.push({'f':f})
870
870
871 with tt.AssertPrints("std. dev. of"):
871 with tt.AssertPrints("std. dev. of"):
872 ip.run_line_magic("timeit", "-n1 f(1)")
872 ip.run_line_magic("timeit", "-n1 f(1)")
873 self.assertEqual(called, {(1,)})
873 self.assertEqual(called, {(1,)})
874 called.clear()
874 called.clear()
875
875
876 with tt.AssertPrints("std. dev. of"):
876 with tt.AssertPrints("std. dev. of"):
877 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
877 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
878 self.assertEqual(called, {(2,), (3,)})
878 self.assertEqual(called, {(2,), (3,)})
879
879
880 class ErrorTransformer(ast.NodeTransformer):
880 class ErrorTransformer(ast.NodeTransformer):
881 """Throws an error when it sees a number."""
881 """Throws an error when it sees a number."""
882
882
883 def visit_Constant(self, node):
883 def visit_Constant(self, node):
884 if isinstance(node.value, int):
884 if isinstance(node.value, int):
885 raise ValueError("test")
885 raise ValueError("test")
886 return node
886 return node
887
887
888
888
889 class TestAstTransformError(unittest.TestCase):
889 class TestAstTransformError(unittest.TestCase):
890 def test_unregistering(self):
890 def test_unregistering(self):
891 err_transformer = ErrorTransformer()
891 err_transformer = ErrorTransformer()
892 ip.ast_transformers.append(err_transformer)
892 ip.ast_transformers.append(err_transformer)
893
893
894 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
894 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
895 ip.run_cell("1 + 2")
895 ip.run_cell("1 + 2")
896
896
897 # This should have been removed.
897 # This should have been removed.
898 self.assertNotIn(err_transformer, ip.ast_transformers)
898 self.assertNotIn(err_transformer, ip.ast_transformers)
899
899
900
900
901 class StringRejector(ast.NodeTransformer):
901 class StringRejector(ast.NodeTransformer):
902 """Throws an InputRejected when it sees a string literal.
902 """Throws an InputRejected when it sees a string literal.
903
903
904 Used to verify that NodeTransformers can signal that a piece of code should
904 Used to verify that NodeTransformers can signal that a piece of code should
905 not be executed by throwing an InputRejected.
905 not be executed by throwing an InputRejected.
906 """
906 """
907
907
908 def visit_Constant(self, node):
908 def visit_Constant(self, node):
909 if isinstance(node.value, str):
909 if isinstance(node.value, str):
910 raise InputRejected("test")
910 raise InputRejected("test")
911 return node
911 return node
912
912
913
913
914 class TestAstTransformInputRejection(unittest.TestCase):
914 class TestAstTransformInputRejection(unittest.TestCase):
915
915
916 def setUp(self):
916 def setUp(self):
917 self.transformer = StringRejector()
917 self.transformer = StringRejector()
918 ip.ast_transformers.append(self.transformer)
918 ip.ast_transformers.append(self.transformer)
919
919
920 def tearDown(self):
920 def tearDown(self):
921 ip.ast_transformers.remove(self.transformer)
921 ip.ast_transformers.remove(self.transformer)
922
922
923 def test_input_rejection(self):
923 def test_input_rejection(self):
924 """Check that NodeTransformers can reject input."""
924 """Check that NodeTransformers can reject input."""
925
925
926 expect_exception_tb = tt.AssertPrints("InputRejected: test")
926 expect_exception_tb = tt.AssertPrints("InputRejected: test")
927 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
927 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
928
928
929 # Run the same check twice to verify that the transformer is not
929 # Run the same check twice to verify that the transformer is not
930 # disabled after raising.
930 # disabled after raising.
931 with expect_exception_tb, expect_no_cell_output:
931 with expect_exception_tb, expect_no_cell_output:
932 ip.run_cell("'unsafe'")
932 ip.run_cell("'unsafe'")
933
933
934 with expect_exception_tb, expect_no_cell_output:
934 with expect_exception_tb, expect_no_cell_output:
935 res = ip.run_cell("'unsafe'")
935 res = ip.run_cell("'unsafe'")
936
936
937 self.assertIsInstance(res.error_before_exec, InputRejected)
937 self.assertIsInstance(res.error_before_exec, InputRejected)
938
938
939 def test__IPYTHON__():
939 def test__IPYTHON__():
940 # This shouldn't raise a NameError, that's all
940 # This shouldn't raise a NameError, that's all
941 __IPYTHON__
941 __IPYTHON__
942
942
943
943
944 class DummyRepr(object):
944 class DummyRepr(object):
945 def __repr__(self):
945 def __repr__(self):
946 return "DummyRepr"
946 return "DummyRepr"
947
947
948 def _repr_html_(self):
948 def _repr_html_(self):
949 return "<b>dummy</b>"
949 return "<b>dummy</b>"
950
950
951 def _repr_javascript_(self):
951 def _repr_javascript_(self):
952 return "console.log('hi');", {'key': 'value'}
952 return "console.log('hi');", {'key': 'value'}
953
953
954
954
955 def test_user_variables():
955 def test_user_variables():
956 # enable all formatters
956 # enable all formatters
957 ip.display_formatter.active_types = ip.display_formatter.format_types
957 ip.display_formatter.active_types = ip.display_formatter.format_types
958
958
959 ip.user_ns['dummy'] = d = DummyRepr()
959 ip.user_ns['dummy'] = d = DummyRepr()
960 keys = {'dummy', 'doesnotexist'}
960 keys = {'dummy', 'doesnotexist'}
961 r = ip.user_expressions({ key:key for key in keys})
961 r = ip.user_expressions({ key:key for key in keys})
962
962
963 assert keys == set(r.keys())
963 assert keys == set(r.keys())
964 dummy = r["dummy"]
964 dummy = r["dummy"]
965 assert {"status", "data", "metadata"} == set(dummy.keys())
965 assert {"status", "data", "metadata"} == set(dummy.keys())
966 assert dummy["status"] == "ok"
966 assert dummy["status"] == "ok"
967 data = dummy["data"]
967 data = dummy["data"]
968 metadata = dummy["metadata"]
968 metadata = dummy["metadata"]
969 assert data.get("text/html") == d._repr_html_()
969 assert data.get("text/html") == d._repr_html_()
970 js, jsmd = d._repr_javascript_()
970 js, jsmd = d._repr_javascript_()
971 assert data.get("application/javascript") == js
971 assert data.get("application/javascript") == js
972 assert metadata.get("application/javascript") == jsmd
972 assert metadata.get("application/javascript") == jsmd
973
973
974 dne = r["doesnotexist"]
974 dne = r["doesnotexist"]
975 assert dne["status"] == "error"
975 assert dne["status"] == "error"
976 assert dne["ename"] == "NameError"
976 assert dne["ename"] == "NameError"
977
977
978 # back to text only
978 # back to text only
979 ip.display_formatter.active_types = ['text/plain']
979 ip.display_formatter.active_types = ['text/plain']
980
980
981 def test_user_expression():
981 def test_user_expression():
982 # enable all formatters
982 # enable all formatters
983 ip.display_formatter.active_types = ip.display_formatter.format_types
983 ip.display_formatter.active_types = ip.display_formatter.format_types
984 query = {
984 query = {
985 'a' : '1 + 2',
985 'a' : '1 + 2',
986 'b' : '1/0',
986 'b' : '1/0',
987 }
987 }
988 r = ip.user_expressions(query)
988 r = ip.user_expressions(query)
989 import pprint
989 import pprint
990 pprint.pprint(r)
990 pprint.pprint(r)
991 assert set(r.keys()) == set(query.keys())
991 assert set(r.keys()) == set(query.keys())
992 a = r["a"]
992 a = r["a"]
993 assert {"status", "data", "metadata"} == set(a.keys())
993 assert {"status", "data", "metadata"} == set(a.keys())
994 assert a["status"] == "ok"
994 assert a["status"] == "ok"
995 data = a["data"]
995 data = a["data"]
996 metadata = a["metadata"]
996 metadata = a["metadata"]
997 assert data.get("text/plain") == "3"
997 assert data.get("text/plain") == "3"
998
998
999 b = r["b"]
999 b = r["b"]
1000 assert b["status"] == "error"
1000 assert b["status"] == "error"
1001 assert b["ename"] == "ZeroDivisionError"
1001 assert b["ename"] == "ZeroDivisionError"
1002
1002
1003 # back to text only
1003 # back to text only
1004 ip.display_formatter.active_types = ['text/plain']
1004 ip.display_formatter.active_types = ['text/plain']
1005
1005
1006
1006
1007 class TestSyntaxErrorTransformer(unittest.TestCase):
1007 class TestSyntaxErrorTransformer(unittest.TestCase):
1008 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1008 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1009
1009
1010 @staticmethod
1010 @staticmethod
1011 def transformer(lines):
1011 def transformer(lines):
1012 for line in lines:
1012 for line in lines:
1013 pos = line.find('syntaxerror')
1013 pos = line.find('syntaxerror')
1014 if pos >= 0:
1014 if pos >= 0:
1015 e = SyntaxError('input contains "syntaxerror"')
1015 e = SyntaxError('input contains "syntaxerror"')
1016 e.text = line
1016 e.text = line
1017 e.offset = pos + 1
1017 e.offset = pos + 1
1018 raise e
1018 raise e
1019 return lines
1019 return lines
1020
1020
1021 def setUp(self):
1021 def setUp(self):
1022 ip.input_transformers_post.append(self.transformer)
1022 ip.input_transformers_post.append(self.transformer)
1023
1023
1024 def tearDown(self):
1024 def tearDown(self):
1025 ip.input_transformers_post.remove(self.transformer)
1025 ip.input_transformers_post.remove(self.transformer)
1026
1026
1027 def test_syntaxerror_input_transformer(self):
1027 def test_syntaxerror_input_transformer(self):
1028 with tt.AssertPrints('1234'):
1028 with tt.AssertPrints('1234'):
1029 ip.run_cell('1234')
1029 ip.run_cell('1234')
1030 with tt.AssertPrints('SyntaxError: invalid syntax'):
1030 with tt.AssertPrints('SyntaxError: invalid syntax'):
1031 ip.run_cell('1 2 3') # plain python syntax error
1031 ip.run_cell('1 2 3') # plain python syntax error
1032 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1032 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1033 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1033 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1034 with tt.AssertPrints('3456'):
1034 with tt.AssertPrints('3456'):
1035 ip.run_cell('3456')
1035 ip.run_cell('3456')
1036
1036
1037
1037
1038 class TestWarningSuppression(unittest.TestCase):
1038 class TestWarningSuppression(unittest.TestCase):
1039 def test_warning_suppression(self):
1039 def test_warning_suppression(self):
1040 ip.run_cell("import warnings")
1040 ip.run_cell("import warnings")
1041 try:
1041 try:
1042 with self.assertWarnsRegex(UserWarning, "asdf"):
1042 with self.assertWarnsRegex(UserWarning, "asdf"):
1043 ip.run_cell("warnings.warn('asdf')")
1043 ip.run_cell("warnings.warn('asdf')")
1044 # Here's the real test -- if we run that again, we should get the
1044 # Here's the real test -- if we run that again, we should get the
1045 # warning again. Traditionally, each warning was only issued once per
1045 # warning again. Traditionally, each warning was only issued once per
1046 # IPython session (approximately), even if the user typed in new and
1046 # IPython session (approximately), even if the user typed in new and
1047 # different code that should have also triggered the warning, leading
1047 # different code that should have also triggered the warning, leading
1048 # to much confusion.
1048 # to much confusion.
1049 with self.assertWarnsRegex(UserWarning, "asdf"):
1049 with self.assertWarnsRegex(UserWarning, "asdf"):
1050 ip.run_cell("warnings.warn('asdf')")
1050 ip.run_cell("warnings.warn('asdf')")
1051 finally:
1051 finally:
1052 ip.run_cell("del warnings")
1052 ip.run_cell("del warnings")
1053
1053
1054
1054
1055 def test_deprecation_warning(self):
1055 def test_deprecation_warning(self):
1056 ip.run_cell("""
1056 ip.run_cell("""
1057 import warnings
1057 import warnings
1058 def wrn():
1058 def wrn():
1059 warnings.warn(
1059 warnings.warn(
1060 "I AM A WARNING",
1060 "I AM A WARNING",
1061 DeprecationWarning
1061 DeprecationWarning
1062 )
1062 )
1063 """)
1063 """)
1064 try:
1064 try:
1065 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1065 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1066 ip.run_cell("wrn()")
1066 ip.run_cell("wrn()")
1067 finally:
1067 finally:
1068 ip.run_cell("del warnings")
1068 ip.run_cell("del warnings")
1069 ip.run_cell("del wrn")
1069 ip.run_cell("del wrn")
1070
1070
1071
1071
1072 class TestImportNoDeprecate(tt.TempFileMixin):
1072 class TestImportNoDeprecate(tt.TempFileMixin):
1073
1073
1074 def setUp(self):
1074 def setUp(self):
1075 """Make a valid python temp file."""
1075 """Make a valid python temp file."""
1076 self.mktmp("""
1076 self.mktmp("""
1077 import warnings
1077 import warnings
1078 def wrn():
1078 def wrn():
1079 warnings.warn(
1079 warnings.warn(
1080 "I AM A WARNING",
1080 "I AM A WARNING",
1081 DeprecationWarning
1081 DeprecationWarning
1082 )
1082 )
1083 """)
1083 """)
1084 super().setUp()
1084 super().setUp()
1085
1085
1086 def test_no_dep(self):
1086 def test_no_dep(self):
1087 """
1087 """
1088 No deprecation warning should be raised from imported functions
1088 No deprecation warning should be raised from imported functions
1089 """
1089 """
1090 ip.run_cell("from {} import wrn".format(self.fname))
1090 ip.run_cell("from {} import wrn".format(self.fname))
1091
1091
1092 with tt.AssertNotPrints("I AM A WARNING"):
1092 with tt.AssertNotPrints("I AM A WARNING"):
1093 ip.run_cell("wrn()")
1093 ip.run_cell("wrn()")
1094 ip.run_cell("del wrn")
1094 ip.run_cell("del wrn")
1095
1095
1096
1096
1097 def test_custom_exc_count():
1097 def test_custom_exc_count():
1098 hook = mock.Mock(return_value=None)
1098 hook = mock.Mock(return_value=None)
1099 ip.set_custom_exc((SyntaxError,), hook)
1099 ip.set_custom_exc((SyntaxError,), hook)
1100 before = ip.execution_count
1100 before = ip.execution_count
1101 ip.run_cell("def foo()", store_history=True)
1101 ip.run_cell("def foo()", store_history=True)
1102 # restore default excepthook
1102 # restore default excepthook
1103 ip.set_custom_exc((), None)
1103 ip.set_custom_exc((), None)
1104 assert hook.call_count == 1
1104 assert hook.call_count == 1
1105 assert ip.execution_count == before + 1
1105 assert ip.execution_count == before + 1
1106
1106
1107
1107
1108 def test_run_cell_async():
1108 def test_run_cell_async():
1109 ip.run_cell("import asyncio")
1109 ip.run_cell("import asyncio")
1110 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1110 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1111 assert asyncio.iscoroutine(coro)
1111 assert asyncio.iscoroutine(coro)
1112 loop = asyncio.new_event_loop()
1112 loop = asyncio.new_event_loop()
1113 result = loop.run_until_complete(coro)
1113 result = loop.run_until_complete(coro)
1114 assert isinstance(result, interactiveshell.ExecutionResult)
1114 assert isinstance(result, interactiveshell.ExecutionResult)
1115 assert result.result == 5
1115 assert result.result == 5
1116
1116
1117
1117
1118 def test_run_cell_await():
1118 def test_run_cell_await():
1119 ip.run_cell("import asyncio")
1119 ip.run_cell("import asyncio")
1120 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1120 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1121 assert ip.user_ns["_"] == 10
1121 assert ip.user_ns["_"] == 10
1122
1122
1123
1123
1124 def test_run_cell_asyncio_run():
1124 def test_run_cell_asyncio_run():
1125 ip.run_cell("import asyncio")
1125 ip.run_cell("import asyncio")
1126 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1126 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1127 assert ip.user_ns["_"] == 1
1127 assert ip.user_ns["_"] == 1
1128 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1128 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1129 assert ip.user_ns["_"] == 2
1129 assert ip.user_ns["_"] == 2
1130 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1130 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1131 assert ip.user_ns["_"] == 3
1131 assert ip.user_ns["_"] == 3
1132
1132
1133
1133
1134 def test_should_run_async():
1134 def test_should_run_async():
1135 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1135 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1136 assert ip.should_run_async("await x", transformed_cell="await x")
1136 assert ip.should_run_async("await x", transformed_cell="await x")
1137 assert ip.should_run_async(
1137 assert ip.should_run_async(
1138 "import asyncio; await asyncio.sleep(1)",
1138 "import asyncio; await asyncio.sleep(1)",
1139 transformed_cell="import asyncio; await asyncio.sleep(1)",
1139 transformed_cell="import asyncio; await asyncio.sleep(1)",
1140 )
1140 )
1141
1141
1142
1142
1143 def test_set_custom_completer():
1143 def test_set_custom_completer():
1144 num_completers = len(ip.Completer.matchers)
1144 num_completers = len(ip.Completer.matchers)
1145
1145
1146 def foo(*args, **kwargs):
1146 def foo(*args, **kwargs):
1147 return "I'm a completer!"
1147 return "I'm a completer!"
1148
1148
1149 ip.set_custom_completer(foo, 0)
1149 ip.set_custom_completer(foo, 0)
1150
1150
1151 # check that we've really added a new completer
1151 # check that we've really added a new completer
1152 assert len(ip.Completer.matchers) == num_completers + 1
1152 assert len(ip.Completer.matchers) == num_completers + 1
1153
1153
1154 # check that the first completer is the function we defined
1154 # check that the first completer is the function we defined
1155 assert ip.Completer.matchers[0]() == "I'm a completer!"
1155 assert ip.Completer.matchers[0]() == "I'm a completer!"
1156
1156
1157 # clean up
1157 # clean up
1158 ip.Completer.custom_matchers.pop()
1158 ip.Completer.custom_matchers.pop()
1159
1159
1160
1160
1161 class TestShowTracebackAttack(unittest.TestCase):
1161 class TestShowTracebackAttack(unittest.TestCase):
1162 """Test that the interactive shell is resilient against the client attack of
1162 """Test that the interactive shell is resilient against the client attack of
1163 manipulating the showtracebacks method. These attacks shouldn't result in an
1163 manipulating the showtracebacks method. These attacks shouldn't result in an
1164 unhandled exception in the kernel."""
1164 unhandled exception in the kernel."""
1165
1165
1166 def setUp(self):
1166 def setUp(self):
1167 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1167 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1168
1168
1169 def tearDown(self):
1169 def tearDown(self):
1170 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1170 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1171
1171
1172 def test_set_show_tracebacks_none(self):
1172 def test_set_show_tracebacks_none(self):
1173 """Test the case of the client setting showtracebacks to None"""
1173 """Test the case of the client setting showtracebacks to None"""
1174
1174
1175 result = ip.run_cell(
1175 result = ip.run_cell(
1176 """
1176 """
1177 import IPython.core.interactiveshell
1177 import IPython.core.interactiveshell
1178 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1178 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1179
1179
1180 assert False, "This should not raise an exception"
1180 assert False, "This should not raise an exception"
1181 """
1181 """
1182 )
1182 )
1183 print(result)
1183 print(result)
1184
1184
1185 assert result.result is None
1185 assert result.result is None
1186 assert isinstance(result.error_in_exec, TypeError)
1186 assert isinstance(result.error_in_exec, TypeError)
1187 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1187 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1188
1188
1189 def test_set_show_tracebacks_noop(self):
1189 def test_set_show_tracebacks_noop(self):
1190 """Test the case of the client setting showtracebacks to a no op lambda"""
1190 """Test the case of the client setting showtracebacks to a no op lambda"""
1191
1191
1192 result = ip.run_cell(
1192 result = ip.run_cell(
1193 """
1193 """
1194 import IPython.core.interactiveshell
1194 import IPython.core.interactiveshell
1195 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1195 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1196
1196
1197 assert False, "This should not raise an exception"
1197 assert False, "This should not raise an exception"
1198 """
1198 """
1199 )
1199 )
1200 print(result)
1200 print(result)
1201
1201
1202 assert result.result is None
1202 assert result.result is None
1203 assert isinstance(result.error_in_exec, AssertionError)
1203 assert isinstance(result.error_in_exec, AssertionError)
1204 assert str(result.error_in_exec) == "This should not raise an exception"
1204 assert str(result.error_in_exec) == "This should not raise an exception"
1205
1205
1206
1206
1207 @skip_if_not_osx
1207 @skip_if_not_osx
1208 def test_enable_gui_osx():
1208 def test_enable_gui_osx():
1209 simple_prompt = ip.simple_prompt
1209 simple_prompt = ip.simple_prompt
1210 ip.simple_prompt = False
1210 ip.simple_prompt = False
1211
1211
1212 ip.enable_gui("osx")
1212 ip.enable_gui("osx")
1213 assert ip.active_eventloop == "osx"
1213 assert ip.active_eventloop == "osx"
1214 ip.enable_gui()
1214 ip.enable_gui()
1215
1215
1216 # The following line fails for IPython <= 8.25.0
1216 # The following line fails for IPython <= 8.25.0
1217 ip.enable_gui("macosx")
1217 ip.enable_gui("macosx")
1218 assert ip.active_eventloop == "osx"
1218 assert ip.active_eventloop == "osx"
1219 ip.enable_gui()
1219 ip.enable_gui()
1220
1220
1221 ip.simple_prompt = simple_prompt
1221 ip.simple_prompt = simple_prompt
@@ -1,90 +1,90
1
1
2 ===========================
2 ===========================
3 Custom input transformation
3 Custom input transformation
4 ===========================
4 ===========================
5
5
6 IPython extends Python syntax to allow things like magic commands, and help with
6 IPython extends Python syntax to allow things like magic commands, and help with
7 the ``?`` syntax. There are several ways to customise how the user's input is
7 the ``?`` syntax. There are several ways to customise how the user's input is
8 processed into Python code to be executed.
8 processed into Python code to be executed.
9
9
10 These hooks are mainly for other projects using IPython as the core of their
10 These hooks are mainly for other projects using IPython as the core of their
11 interactive interface. Using them carelessly can easily break IPython!
11 interactive interface. Using them carelessly can easily break IPython!
12
12
13 String based transformations
13 String based transformations
14 ============================
14 ============================
15
15
16 .. currentmodule:: IPython.core.inputtransforms
16 .. currentmodule:: IPython.core.inputtransforms
17
17
18 When the user enters code, it is first processed as a string. By the
18 When the user enters code, it is first processed as a string. By the
19 end of this stage, it must be valid Python syntax.
19 end of this stage, it must be valid Python syntax.
20
20
21 .. versionchanged:: 7.0
21 .. versionchanged:: 7.0
22
22
23 The API for string and token-based transformations has been completely
23 The API for string and token-based transformations has been completely
24 redesigned. Any third party code extending input transformation will need to
24 redesigned. Any third party code extending input transformation will need to
25 be rewritten. The new API is, hopefully, simpler.
25 be rewritten. The new API is, hopefully, simpler.
26
26
27 String based transformations are functions which accept a list of strings:
27 String based transformations are functions which accept a list of strings:
28 each string is a single line of the input cell, including its line ending.
28 each string is a single line of the input cell, including its line ending.
29 The transformation function should return output in the same structure.
29 The transformation function should return output in the same structure.
30
30
31 These transformations are in two groups, accessible as attributes of
31 These transformations are in two groups, accessible as attributes of
32 the :class:`~IPython.core.interactiveshell.InteractiveShell` instance.
32 the :class:`~IPython.core.interactiveshell.InteractiveShell` instance.
33 Each group is a list of transformation functions.
33 Each group is a list of transformation functions.
34
34
35 * ``input_transformers_cleanup`` run first on input, to do things like stripping
35 * ``input_transformers_cleanup`` run first on input, to do things like stripping
36 prompts and leading indents from copied code. It may not be possible at this
36 prompts and leading indents from copied code. It may not be possible at this
37 stage to parse the input as valid Python code.
37 stage to parse the input as valid Python code.
38 * Then IPython runs its own transformations to handle its special syntax, like
38 * Then IPython runs its own transformations to handle its special syntax, like
39 ``%magics`` and ``!system`` commands. This part does not expose extension
39 ``%magics`` and ``!system`` commands. This part does not expose extension
40 points.
40 points.
41 * ``input_transformers_post`` run as the last step, to do things like converting
41 * ``input_transformers_post`` run as the last step, to do things like converting
42 float literals into decimal objects. These may attempt to parse the input as
42 float literals into decimal objects. These may attempt to parse the input as
43 Python code.
43 Python code.
44
44
45 These transformers may raise :exc:`SyntaxError` if the input code is invalid, but
45 These transformers may raise :exc:`SyntaxError` if the input code is invalid, but
46 in most cases it is clearer to pass unrecognised code through unmodified and let
46 in most cases it is clearer to pass unrecognised code through unmodified and let
47 Python's own parser decide whether it is valid.
47 Python's own parser decide whether it is valid.
48
48
49 For example, imagine we want to obfuscate our code by reversing each line, so
49 For example, imagine we want to obfuscate our code by reversing each line, so
50 we'd write ``)5(f =+ a`` instead of ``a += f(5)``. Here's how we could swap it
50 we'd write ``)5(f =+ a`` instead of ``a += f(5)``. Here's how we could swap it
51 back the right way before IPython tries to run it::
51 back the right way before IPython tries to run it::
52
52
53 def reverse_line_chars(lines):
53 def reverse_line_chars(lines):
54 new_lines = []
54 new_lines = []
55 for line in lines:
55 for line in lines:
56 chars = line[:-1] # the newline needs to stay at the end
56 chars = line[:-1] # the newline needs to stay at the end
57 new_lines.append(chars[::-1] + '\n')
57 new_lines.append(chars[::-1] + '\n')
58 return new_lines
58 return new_lines
59
59
60 To start using this::
60 To start using this::
61
61
62 ip = get_ipython()
62 ip = get_ipython()
63 ip.input_transformers_cleanup.append(reverse_line_chars)
63 ip.input_transformers_cleanup.append(reverse_line_chars)
64
64
65 .. versionadded:: 7.17
65 .. versionadded:: 7.17
66
66
67 input_transformers can now have an attribute ``has_side_effects`` set to
67 input_transformers can now have an attribute ``has_side_effects`` set to
68 `True`, which will prevent the transformers from being ran when IPython is
68 `True`, which will prevent the transformers from being ran when IPython is
69 trying to guess whether the user input is complete.
69 trying to guess whether the user input is complete.
70
70
71
71
72
72
73 AST transformations
73 AST transformations
74 ===================
74 ===================
75
75
76 After the code has been parsed as Python syntax, you can use Python's powerful
76 After the code has been parsed as Python syntax, you can use Python's powerful
77 *Abstract Syntax Tree* tools to modify it. Subclass :class:`ast.NodeTransformer`,
77 *Abstract Syntax Tree* tools to modify it. Subclass :class:`ast.NodeTransformer`,
78 and add an instance to ``shell.ast_transformers``.
78 and add an instance to ``shell.ast_transformers``.
79
79
80 This example wraps integer literals in an ``Integer`` class, which is useful for
80 This example wraps integer literals in an ``Integer`` class, which is useful for
81 mathematical frameworks that want to handle e.g. ``1/3`` as a precise fraction::
81 mathematical frameworks that want to handle e.g. ``1/3`` as a precise fraction::
82
82
83
83
84 class IntegerWrapper(ast.NodeTransformer):
84 class IntegerWrapper(ast.NodeTransformer):
85 """Wraps all integers in a call to Integer()"""
85 """Wraps all integers in a call to Integer()"""
86 def visit_Num(self, node):
86 def visit_Num(self, node):
87 if isinstance(node.n, int):
87 if isinstance(node.value, int):
88 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
88 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
89 args=[node], keywords=[])
89 args=[node], keywords=[])
90 return node
90 return node
General Comments 0
You need to be logged in to leave comments. Login now