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