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