##// END OF EJS Templates
Fix lint warnings
takuya fujiwara -
Show More
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -1,1060 +1,1060 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 def mock_print_func(value, sep=' ', end='\n', file=sys.stdout, flush=False):
455 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
456 values.append(value)
456 values.append(value)
457 if value == chr(0xd8ff):
457 if value == chr(0xD8FF):
458 raise UnicodeEncodeError("utf-8", chr(0xd8ff), 0, 1, "")
458 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
459
459
460 # mock builtins.print
460 # mock builtins.print
461 mocked_print.side_effect = mock_print_func
461 mocked_print.side_effect = mock_print_func
462
462
463 # ip._showtraceback() is replaced in globalipapp.py.
463 # ip._showtraceback() is replaced in globalipapp.py.
464 # Call original method to test.
464 # Call original method to test.
465 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xd8ff))
465 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
466
466
467 self.assertEqual(mocked_print.call_count, 2)
467 self.assertEqual(mocked_print.call_count, 2)
468 self.assertEqual(values, [chr(0xd8ff), '\\ud8ff'])
468 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
469
469
470 def test_mktempfile(self):
470 def test_mktempfile(self):
471 filename = ip.mktempfile()
471 filename = ip.mktempfile()
472 # Check that we can open the file again on Windows
472 # Check that we can open the file again on Windows
473 with open(filename, 'w') as f:
473 with open(filename, 'w') as f:
474 f.write('abc')
474 f.write('abc')
475
475
476 filename = ip.mktempfile(data='blah')
476 filename = ip.mktempfile(data='blah')
477 with open(filename, 'r') as f:
477 with open(filename, 'r') as f:
478 self.assertEqual(f.read(), 'blah')
478 self.assertEqual(f.read(), 'blah')
479
479
480 def test_new_main_mod(self):
480 def test_new_main_mod(self):
481 # Smoketest to check that this accepts a unicode module name
481 # Smoketest to check that this accepts a unicode module name
482 name = u'jiefmw'
482 name = u'jiefmw'
483 mod = ip.new_main_mod(u'%s.py' % name, name)
483 mod = ip.new_main_mod(u'%s.py' % name, name)
484 self.assertEqual(mod.__name__, name)
484 self.assertEqual(mod.__name__, name)
485
485
486 def test_get_exception_only(self):
486 def test_get_exception_only(self):
487 try:
487 try:
488 raise KeyboardInterrupt
488 raise KeyboardInterrupt
489 except KeyboardInterrupt:
489 except KeyboardInterrupt:
490 msg = ip.get_exception_only()
490 msg = ip.get_exception_only()
491 self.assertEqual(msg, 'KeyboardInterrupt\n')
491 self.assertEqual(msg, 'KeyboardInterrupt\n')
492
492
493 try:
493 try:
494 raise DerivedInterrupt("foo")
494 raise DerivedInterrupt("foo")
495 except KeyboardInterrupt:
495 except KeyboardInterrupt:
496 msg = ip.get_exception_only()
496 msg = ip.get_exception_only()
497 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
497 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
498
498
499 def test_inspect_text(self):
499 def test_inspect_text(self):
500 ip.run_cell('a = 5')
500 ip.run_cell('a = 5')
501 text = ip.object_inspect_text('a')
501 text = ip.object_inspect_text('a')
502 self.assertIsInstance(text, str)
502 self.assertIsInstance(text, str)
503
503
504 def test_last_execution_result(self):
504 def test_last_execution_result(self):
505 """ Check that last execution result gets set correctly (GH-10702) """
505 """ Check that last execution result gets set correctly (GH-10702) """
506 result = ip.run_cell('a = 5; a')
506 result = ip.run_cell('a = 5; a')
507 self.assertTrue(ip.last_execution_succeeded)
507 self.assertTrue(ip.last_execution_succeeded)
508 self.assertEqual(ip.last_execution_result.result, 5)
508 self.assertEqual(ip.last_execution_result.result, 5)
509
509
510 result = ip.run_cell('a = x_invalid_id_x')
510 result = ip.run_cell('a = x_invalid_id_x')
511 self.assertFalse(ip.last_execution_succeeded)
511 self.assertFalse(ip.last_execution_succeeded)
512 self.assertFalse(ip.last_execution_result.success)
512 self.assertFalse(ip.last_execution_result.success)
513 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
513 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
514
514
515 def test_reset_aliasing(self):
515 def test_reset_aliasing(self):
516 """ Check that standard posix aliases work after %reset. """
516 """ Check that standard posix aliases work after %reset. """
517 if os.name != 'posix':
517 if os.name != 'posix':
518 return
518 return
519
519
520 ip.reset()
520 ip.reset()
521 for cmd in ('clear', 'more', 'less', 'man'):
521 for cmd in ('clear', 'more', 'less', 'man'):
522 res = ip.run_cell('%' + cmd)
522 res = ip.run_cell('%' + cmd)
523 self.assertEqual(res.success, True)
523 self.assertEqual(res.success, True)
524
524
525
525
526 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
526 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
527
527
528 @onlyif_unicode_paths
528 @onlyif_unicode_paths
529 def setUp(self):
529 def setUp(self):
530 self.BASETESTDIR = tempfile.mkdtemp()
530 self.BASETESTDIR = tempfile.mkdtemp()
531 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
531 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
532 os.mkdir(self.TESTDIR)
532 os.mkdir(self.TESTDIR)
533 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
533 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
534 sfile.write("pass\n")
534 sfile.write("pass\n")
535 self.oldpath = os.getcwd()
535 self.oldpath = os.getcwd()
536 os.chdir(self.TESTDIR)
536 os.chdir(self.TESTDIR)
537 self.fname = u"åäötestscript.py"
537 self.fname = u"åäötestscript.py"
538
538
539 def tearDown(self):
539 def tearDown(self):
540 os.chdir(self.oldpath)
540 os.chdir(self.oldpath)
541 shutil.rmtree(self.BASETESTDIR)
541 shutil.rmtree(self.BASETESTDIR)
542
542
543 @onlyif_unicode_paths
543 @onlyif_unicode_paths
544 def test_1(self):
544 def test_1(self):
545 """Test safe_execfile with non-ascii path
545 """Test safe_execfile with non-ascii path
546 """
546 """
547 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
547 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
548
548
549 class ExitCodeChecks(tt.TempFileMixin):
549 class ExitCodeChecks(tt.TempFileMixin):
550
550
551 def setUp(self):
551 def setUp(self):
552 self.system = ip.system_raw
552 self.system = ip.system_raw
553
553
554 def test_exit_code_ok(self):
554 def test_exit_code_ok(self):
555 self.system('exit 0')
555 self.system('exit 0')
556 self.assertEqual(ip.user_ns['_exit_code'], 0)
556 self.assertEqual(ip.user_ns['_exit_code'], 0)
557
557
558 def test_exit_code_error(self):
558 def test_exit_code_error(self):
559 self.system('exit 1')
559 self.system('exit 1')
560 self.assertEqual(ip.user_ns['_exit_code'], 1)
560 self.assertEqual(ip.user_ns['_exit_code'], 1)
561
561
562 @skipif(not hasattr(signal, 'SIGALRM'))
562 @skipif(not hasattr(signal, 'SIGALRM'))
563 def test_exit_code_signal(self):
563 def test_exit_code_signal(self):
564 self.mktmp("import signal, time\n"
564 self.mktmp("import signal, time\n"
565 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
565 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
566 "time.sleep(1)\n")
566 "time.sleep(1)\n")
567 self.system("%s %s" % (sys.executable, self.fname))
567 self.system("%s %s" % (sys.executable, self.fname))
568 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
568 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
569
569
570 @onlyif_cmds_exist("csh")
570 @onlyif_cmds_exist("csh")
571 def test_exit_code_signal_csh(self):
571 def test_exit_code_signal_csh(self):
572 SHELL = os.environ.get('SHELL', None)
572 SHELL = os.environ.get('SHELL', None)
573 os.environ['SHELL'] = find_cmd("csh")
573 os.environ['SHELL'] = find_cmd("csh")
574 try:
574 try:
575 self.test_exit_code_signal()
575 self.test_exit_code_signal()
576 finally:
576 finally:
577 if SHELL is not None:
577 if SHELL is not None:
578 os.environ['SHELL'] = SHELL
578 os.environ['SHELL'] = SHELL
579 else:
579 else:
580 del os.environ['SHELL']
580 del os.environ['SHELL']
581
581
582
582
583 class TestSystemRaw(ExitCodeChecks):
583 class TestSystemRaw(ExitCodeChecks):
584
584
585 def setUp(self):
585 def setUp(self):
586 super().setUp()
586 super().setUp()
587 self.system = ip.system_raw
587 self.system = ip.system_raw
588
588
589 @onlyif_unicode_paths
589 @onlyif_unicode_paths
590 def test_1(self):
590 def test_1(self):
591 """Test system_raw with non-ascii cmd
591 """Test system_raw with non-ascii cmd
592 """
592 """
593 cmd = u'''python -c "'åäö'" '''
593 cmd = u'''python -c "'åäö'" '''
594 ip.system_raw(cmd)
594 ip.system_raw(cmd)
595
595
596 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
596 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
597 @mock.patch('os.system', side_effect=KeyboardInterrupt)
597 @mock.patch('os.system', side_effect=KeyboardInterrupt)
598 def test_control_c(self, *mocks):
598 def test_control_c(self, *mocks):
599 try:
599 try:
600 self.system("sleep 1 # wont happen")
600 self.system("sleep 1 # wont happen")
601 except KeyboardInterrupt:
601 except KeyboardInterrupt:
602 self.fail("system call should intercept "
602 self.fail("system call should intercept "
603 "keyboard interrupt from subprocess.call")
603 "keyboard interrupt from subprocess.call")
604 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
604 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
605
605
606 # TODO: Exit codes are currently ignored on Windows.
606 # TODO: Exit codes are currently ignored on Windows.
607 class TestSystemPipedExitCode(ExitCodeChecks):
607 class TestSystemPipedExitCode(ExitCodeChecks):
608
608
609 def setUp(self):
609 def setUp(self):
610 super().setUp()
610 super().setUp()
611 self.system = ip.system_piped
611 self.system = ip.system_piped
612
612
613 @skip_win32
613 @skip_win32
614 def test_exit_code_ok(self):
614 def test_exit_code_ok(self):
615 ExitCodeChecks.test_exit_code_ok(self)
615 ExitCodeChecks.test_exit_code_ok(self)
616
616
617 @skip_win32
617 @skip_win32
618 def test_exit_code_error(self):
618 def test_exit_code_error(self):
619 ExitCodeChecks.test_exit_code_error(self)
619 ExitCodeChecks.test_exit_code_error(self)
620
620
621 @skip_win32
621 @skip_win32
622 def test_exit_code_signal(self):
622 def test_exit_code_signal(self):
623 ExitCodeChecks.test_exit_code_signal(self)
623 ExitCodeChecks.test_exit_code_signal(self)
624
624
625 class TestModules(tt.TempFileMixin):
625 class TestModules(tt.TempFileMixin):
626 def test_extraneous_loads(self):
626 def test_extraneous_loads(self):
627 """Test we're not loading modules on startup that we shouldn't.
627 """Test we're not loading modules on startup that we shouldn't.
628 """
628 """
629 self.mktmp("import sys\n"
629 self.mktmp("import sys\n"
630 "print('numpy' in sys.modules)\n"
630 "print('numpy' in sys.modules)\n"
631 "print('ipyparallel' in sys.modules)\n"
631 "print('ipyparallel' in sys.modules)\n"
632 "print('ipykernel' in sys.modules)\n"
632 "print('ipykernel' in sys.modules)\n"
633 )
633 )
634 out = "False\nFalse\nFalse\n"
634 out = "False\nFalse\nFalse\n"
635 tt.ipexec_validate(self.fname, out)
635 tt.ipexec_validate(self.fname, out)
636
636
637 class Negator(ast.NodeTransformer):
637 class Negator(ast.NodeTransformer):
638 """Negates all number literals in an AST."""
638 """Negates all number literals in an AST."""
639
639
640 # for python 3.7 and earlier
640 # for python 3.7 and earlier
641 def visit_Num(self, node):
641 def visit_Num(self, node):
642 node.n = -node.n
642 node.n = -node.n
643 return node
643 return node
644
644
645 # for python 3.8+
645 # for python 3.8+
646 def visit_Constant(self, node):
646 def visit_Constant(self, node):
647 if isinstance(node.value, int):
647 if isinstance(node.value, int):
648 return self.visit_Num(node)
648 return self.visit_Num(node)
649 return node
649 return node
650
650
651 class TestAstTransform(unittest.TestCase):
651 class TestAstTransform(unittest.TestCase):
652 def setUp(self):
652 def setUp(self):
653 self.negator = Negator()
653 self.negator = Negator()
654 ip.ast_transformers.append(self.negator)
654 ip.ast_transformers.append(self.negator)
655
655
656 def tearDown(self):
656 def tearDown(self):
657 ip.ast_transformers.remove(self.negator)
657 ip.ast_transformers.remove(self.negator)
658
658
659 def test_run_cell(self):
659 def test_run_cell(self):
660 with tt.AssertPrints('-34'):
660 with tt.AssertPrints('-34'):
661 ip.run_cell('print (12 + 22)')
661 ip.run_cell('print (12 + 22)')
662
662
663 # A named reference to a number shouldn't be transformed.
663 # A named reference to a number shouldn't be transformed.
664 ip.user_ns['n'] = 55
664 ip.user_ns['n'] = 55
665 with tt.AssertNotPrints('-55'):
665 with tt.AssertNotPrints('-55'):
666 ip.run_cell('print (n)')
666 ip.run_cell('print (n)')
667
667
668 def test_timeit(self):
668 def test_timeit(self):
669 called = set()
669 called = set()
670 def f(x):
670 def f(x):
671 called.add(x)
671 called.add(x)
672 ip.push({'f':f})
672 ip.push({'f':f})
673
673
674 with tt.AssertPrints("std. dev. of"):
674 with tt.AssertPrints("std. dev. of"):
675 ip.run_line_magic("timeit", "-n1 f(1)")
675 ip.run_line_magic("timeit", "-n1 f(1)")
676 self.assertEqual(called, {-1})
676 self.assertEqual(called, {-1})
677 called.clear()
677 called.clear()
678
678
679 with tt.AssertPrints("std. dev. of"):
679 with tt.AssertPrints("std. dev. of"):
680 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
680 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
681 self.assertEqual(called, {-2, -3})
681 self.assertEqual(called, {-2, -3})
682
682
683 def test_time(self):
683 def test_time(self):
684 called = []
684 called = []
685 def f(x):
685 def f(x):
686 called.append(x)
686 called.append(x)
687 ip.push({'f':f})
687 ip.push({'f':f})
688
688
689 # Test with an expression
689 # Test with an expression
690 with tt.AssertPrints("Wall time: "):
690 with tt.AssertPrints("Wall time: "):
691 ip.run_line_magic("time", "f(5+9)")
691 ip.run_line_magic("time", "f(5+9)")
692 self.assertEqual(called, [-14])
692 self.assertEqual(called, [-14])
693 called[:] = []
693 called[:] = []
694
694
695 # Test with a statement (different code path)
695 # Test with a statement (different code path)
696 with tt.AssertPrints("Wall time: "):
696 with tt.AssertPrints("Wall time: "):
697 ip.run_line_magic("time", "a = f(-3 + -2)")
697 ip.run_line_magic("time", "a = f(-3 + -2)")
698 self.assertEqual(called, [5])
698 self.assertEqual(called, [5])
699
699
700 def test_macro(self):
700 def test_macro(self):
701 ip.push({'a':10})
701 ip.push({'a':10})
702 # The AST transformation makes this do a+=-1
702 # The AST transformation makes this do a+=-1
703 ip.define_macro("amacro", "a+=1\nprint(a)")
703 ip.define_macro("amacro", "a+=1\nprint(a)")
704
704
705 with tt.AssertPrints("9"):
705 with tt.AssertPrints("9"):
706 ip.run_cell("amacro")
706 ip.run_cell("amacro")
707 with tt.AssertPrints("8"):
707 with tt.AssertPrints("8"):
708 ip.run_cell("amacro")
708 ip.run_cell("amacro")
709
709
710 class TestMiscTransform(unittest.TestCase):
710 class TestMiscTransform(unittest.TestCase):
711
711
712
712
713 def test_transform_only_once(self):
713 def test_transform_only_once(self):
714 cleanup = 0
714 cleanup = 0
715 line_t = 0
715 line_t = 0
716 def count_cleanup(lines):
716 def count_cleanup(lines):
717 nonlocal cleanup
717 nonlocal cleanup
718 cleanup += 1
718 cleanup += 1
719 return lines
719 return lines
720
720
721 def count_line_t(lines):
721 def count_line_t(lines):
722 nonlocal line_t
722 nonlocal line_t
723 line_t += 1
723 line_t += 1
724 return lines
724 return lines
725
725
726 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
726 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
727 ip.input_transformer_manager.line_transforms.append(count_line_t)
727 ip.input_transformer_manager.line_transforms.append(count_line_t)
728
728
729 ip.run_cell('1')
729 ip.run_cell('1')
730
730
731 assert cleanup == 1
731 assert cleanup == 1
732 assert line_t == 1
732 assert line_t == 1
733
733
734 class IntegerWrapper(ast.NodeTransformer):
734 class IntegerWrapper(ast.NodeTransformer):
735 """Wraps all integers in a call to Integer()"""
735 """Wraps all integers in a call to Integer()"""
736
736
737 # for Python 3.7 and earlier
737 # for Python 3.7 and earlier
738
738
739 # for Python 3.7 and earlier
739 # for Python 3.7 and earlier
740 def visit_Num(self, node):
740 def visit_Num(self, node):
741 if isinstance(node.n, int):
741 if isinstance(node.n, int):
742 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
742 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
743 args=[node], keywords=[])
743 args=[node], keywords=[])
744 return node
744 return node
745
745
746 # For Python 3.8+
746 # For Python 3.8+
747 def visit_Constant(self, node):
747 def visit_Constant(self, node):
748 if isinstance(node.value, int):
748 if isinstance(node.value, int):
749 return self.visit_Num(node)
749 return self.visit_Num(node)
750 return node
750 return node
751
751
752
752
753 class TestAstTransform2(unittest.TestCase):
753 class TestAstTransform2(unittest.TestCase):
754 def setUp(self):
754 def setUp(self):
755 self.intwrapper = IntegerWrapper()
755 self.intwrapper = IntegerWrapper()
756 ip.ast_transformers.append(self.intwrapper)
756 ip.ast_transformers.append(self.intwrapper)
757
757
758 self.calls = []
758 self.calls = []
759 def Integer(*args):
759 def Integer(*args):
760 self.calls.append(args)
760 self.calls.append(args)
761 return args
761 return args
762 ip.push({"Integer": Integer})
762 ip.push({"Integer": Integer})
763
763
764 def tearDown(self):
764 def tearDown(self):
765 ip.ast_transformers.remove(self.intwrapper)
765 ip.ast_transformers.remove(self.intwrapper)
766 del ip.user_ns['Integer']
766 del ip.user_ns['Integer']
767
767
768 def test_run_cell(self):
768 def test_run_cell(self):
769 ip.run_cell("n = 2")
769 ip.run_cell("n = 2")
770 self.assertEqual(self.calls, [(2,)])
770 self.assertEqual(self.calls, [(2,)])
771
771
772 # This shouldn't throw an error
772 # This shouldn't throw an error
773 ip.run_cell("o = 2.0")
773 ip.run_cell("o = 2.0")
774 self.assertEqual(ip.user_ns['o'], 2.0)
774 self.assertEqual(ip.user_ns['o'], 2.0)
775
775
776 def test_timeit(self):
776 def test_timeit(self):
777 called = set()
777 called = set()
778 def f(x):
778 def f(x):
779 called.add(x)
779 called.add(x)
780 ip.push({'f':f})
780 ip.push({'f':f})
781
781
782 with tt.AssertPrints("std. dev. of"):
782 with tt.AssertPrints("std. dev. of"):
783 ip.run_line_magic("timeit", "-n1 f(1)")
783 ip.run_line_magic("timeit", "-n1 f(1)")
784 self.assertEqual(called, {(1,)})
784 self.assertEqual(called, {(1,)})
785 called.clear()
785 called.clear()
786
786
787 with tt.AssertPrints("std. dev. of"):
787 with tt.AssertPrints("std. dev. of"):
788 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
788 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
789 self.assertEqual(called, {(2,), (3,)})
789 self.assertEqual(called, {(2,), (3,)})
790
790
791 class ErrorTransformer(ast.NodeTransformer):
791 class ErrorTransformer(ast.NodeTransformer):
792 """Throws an error when it sees a number."""
792 """Throws an error when it sees a number."""
793
793
794 # for Python 3.7 and earlier
794 # for Python 3.7 and earlier
795 def visit_Num(self, node):
795 def visit_Num(self, node):
796 raise ValueError("test")
796 raise ValueError("test")
797
797
798 # for Python 3.8+
798 # for Python 3.8+
799 def visit_Constant(self, node):
799 def visit_Constant(self, node):
800 if isinstance(node.value, int):
800 if isinstance(node.value, int):
801 return self.visit_Num(node)
801 return self.visit_Num(node)
802 return node
802 return node
803
803
804
804
805 class TestAstTransformError(unittest.TestCase):
805 class TestAstTransformError(unittest.TestCase):
806 def test_unregistering(self):
806 def test_unregistering(self):
807 err_transformer = ErrorTransformer()
807 err_transformer = ErrorTransformer()
808 ip.ast_transformers.append(err_transformer)
808 ip.ast_transformers.append(err_transformer)
809
809
810 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
810 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
811 ip.run_cell("1 + 2")
811 ip.run_cell("1 + 2")
812
812
813 # This should have been removed.
813 # This should have been removed.
814 nt.assert_not_in(err_transformer, ip.ast_transformers)
814 nt.assert_not_in(err_transformer, ip.ast_transformers)
815
815
816
816
817 class StringRejector(ast.NodeTransformer):
817 class StringRejector(ast.NodeTransformer):
818 """Throws an InputRejected when it sees a string literal.
818 """Throws an InputRejected when it sees a string literal.
819
819
820 Used to verify that NodeTransformers can signal that a piece of code should
820 Used to verify that NodeTransformers can signal that a piece of code should
821 not be executed by throwing an InputRejected.
821 not be executed by throwing an InputRejected.
822 """
822 """
823
823
824 #for python 3.7 and earlier
824 #for python 3.7 and earlier
825 def visit_Str(self, node):
825 def visit_Str(self, node):
826 raise InputRejected("test")
826 raise InputRejected("test")
827
827
828 # 3.8 only
828 # 3.8 only
829 def visit_Constant(self, node):
829 def visit_Constant(self, node):
830 if isinstance(node.value, str):
830 if isinstance(node.value, str):
831 raise InputRejected("test")
831 raise InputRejected("test")
832 return node
832 return node
833
833
834
834
835 class TestAstTransformInputRejection(unittest.TestCase):
835 class TestAstTransformInputRejection(unittest.TestCase):
836
836
837 def setUp(self):
837 def setUp(self):
838 self.transformer = StringRejector()
838 self.transformer = StringRejector()
839 ip.ast_transformers.append(self.transformer)
839 ip.ast_transformers.append(self.transformer)
840
840
841 def tearDown(self):
841 def tearDown(self):
842 ip.ast_transformers.remove(self.transformer)
842 ip.ast_transformers.remove(self.transformer)
843
843
844 def test_input_rejection(self):
844 def test_input_rejection(self):
845 """Check that NodeTransformers can reject input."""
845 """Check that NodeTransformers can reject input."""
846
846
847 expect_exception_tb = tt.AssertPrints("InputRejected: test")
847 expect_exception_tb = tt.AssertPrints("InputRejected: test")
848 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
848 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
849
849
850 # Run the same check twice to verify that the transformer is not
850 # Run the same check twice to verify that the transformer is not
851 # disabled after raising.
851 # disabled after raising.
852 with expect_exception_tb, expect_no_cell_output:
852 with expect_exception_tb, expect_no_cell_output:
853 ip.run_cell("'unsafe'")
853 ip.run_cell("'unsafe'")
854
854
855 with expect_exception_tb, expect_no_cell_output:
855 with expect_exception_tb, expect_no_cell_output:
856 res = ip.run_cell("'unsafe'")
856 res = ip.run_cell("'unsafe'")
857
857
858 self.assertIsInstance(res.error_before_exec, InputRejected)
858 self.assertIsInstance(res.error_before_exec, InputRejected)
859
859
860 def test__IPYTHON__():
860 def test__IPYTHON__():
861 # This shouldn't raise a NameError, that's all
861 # This shouldn't raise a NameError, that's all
862 __IPYTHON__
862 __IPYTHON__
863
863
864
864
865 class DummyRepr(object):
865 class DummyRepr(object):
866 def __repr__(self):
866 def __repr__(self):
867 return "DummyRepr"
867 return "DummyRepr"
868
868
869 def _repr_html_(self):
869 def _repr_html_(self):
870 return "<b>dummy</b>"
870 return "<b>dummy</b>"
871
871
872 def _repr_javascript_(self):
872 def _repr_javascript_(self):
873 return "console.log('hi');", {'key': 'value'}
873 return "console.log('hi');", {'key': 'value'}
874
874
875
875
876 def test_user_variables():
876 def test_user_variables():
877 # enable all formatters
877 # enable all formatters
878 ip.display_formatter.active_types = ip.display_formatter.format_types
878 ip.display_formatter.active_types = ip.display_formatter.format_types
879
879
880 ip.user_ns['dummy'] = d = DummyRepr()
880 ip.user_ns['dummy'] = d = DummyRepr()
881 keys = {'dummy', 'doesnotexist'}
881 keys = {'dummy', 'doesnotexist'}
882 r = ip.user_expressions({ key:key for key in keys})
882 r = ip.user_expressions({ key:key for key in keys})
883
883
884 nt.assert_equal(keys, set(r.keys()))
884 nt.assert_equal(keys, set(r.keys()))
885 dummy = r['dummy']
885 dummy = r['dummy']
886 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
886 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
887 nt.assert_equal(dummy['status'], 'ok')
887 nt.assert_equal(dummy['status'], 'ok')
888 data = dummy['data']
888 data = dummy['data']
889 metadata = dummy['metadata']
889 metadata = dummy['metadata']
890 nt.assert_equal(data.get('text/html'), d._repr_html_())
890 nt.assert_equal(data.get('text/html'), d._repr_html_())
891 js, jsmd = d._repr_javascript_()
891 js, jsmd = d._repr_javascript_()
892 nt.assert_equal(data.get('application/javascript'), js)
892 nt.assert_equal(data.get('application/javascript'), js)
893 nt.assert_equal(metadata.get('application/javascript'), jsmd)
893 nt.assert_equal(metadata.get('application/javascript'), jsmd)
894
894
895 dne = r['doesnotexist']
895 dne = r['doesnotexist']
896 nt.assert_equal(dne['status'], 'error')
896 nt.assert_equal(dne['status'], 'error')
897 nt.assert_equal(dne['ename'], 'NameError')
897 nt.assert_equal(dne['ename'], 'NameError')
898
898
899 # back to text only
899 # back to text only
900 ip.display_formatter.active_types = ['text/plain']
900 ip.display_formatter.active_types = ['text/plain']
901
901
902 def test_user_expression():
902 def test_user_expression():
903 # enable all formatters
903 # enable all formatters
904 ip.display_formatter.active_types = ip.display_formatter.format_types
904 ip.display_formatter.active_types = ip.display_formatter.format_types
905 query = {
905 query = {
906 'a' : '1 + 2',
906 'a' : '1 + 2',
907 'b' : '1/0',
907 'b' : '1/0',
908 }
908 }
909 r = ip.user_expressions(query)
909 r = ip.user_expressions(query)
910 import pprint
910 import pprint
911 pprint.pprint(r)
911 pprint.pprint(r)
912 nt.assert_equal(set(r.keys()), set(query.keys()))
912 nt.assert_equal(set(r.keys()), set(query.keys()))
913 a = r['a']
913 a = r['a']
914 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
914 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
915 nt.assert_equal(a['status'], 'ok')
915 nt.assert_equal(a['status'], 'ok')
916 data = a['data']
916 data = a['data']
917 metadata = a['metadata']
917 metadata = a['metadata']
918 nt.assert_equal(data.get('text/plain'), '3')
918 nt.assert_equal(data.get('text/plain'), '3')
919
919
920 b = r['b']
920 b = r['b']
921 nt.assert_equal(b['status'], 'error')
921 nt.assert_equal(b['status'], 'error')
922 nt.assert_equal(b['ename'], 'ZeroDivisionError')
922 nt.assert_equal(b['ename'], 'ZeroDivisionError')
923
923
924 # back to text only
924 # back to text only
925 ip.display_formatter.active_types = ['text/plain']
925 ip.display_formatter.active_types = ['text/plain']
926
926
927
927
928 class TestSyntaxErrorTransformer(unittest.TestCase):
928 class TestSyntaxErrorTransformer(unittest.TestCase):
929 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
929 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
930
930
931 @staticmethod
931 @staticmethod
932 def transformer(lines):
932 def transformer(lines):
933 for line in lines:
933 for line in lines:
934 pos = line.find('syntaxerror')
934 pos = line.find('syntaxerror')
935 if pos >= 0:
935 if pos >= 0:
936 e = SyntaxError('input contains "syntaxerror"')
936 e = SyntaxError('input contains "syntaxerror"')
937 e.text = line
937 e.text = line
938 e.offset = pos + 1
938 e.offset = pos + 1
939 raise e
939 raise e
940 return lines
940 return lines
941
941
942 def setUp(self):
942 def setUp(self):
943 ip.input_transformers_post.append(self.transformer)
943 ip.input_transformers_post.append(self.transformer)
944
944
945 def tearDown(self):
945 def tearDown(self):
946 ip.input_transformers_post.remove(self.transformer)
946 ip.input_transformers_post.remove(self.transformer)
947
947
948 def test_syntaxerror_input_transformer(self):
948 def test_syntaxerror_input_transformer(self):
949 with tt.AssertPrints('1234'):
949 with tt.AssertPrints('1234'):
950 ip.run_cell('1234')
950 ip.run_cell('1234')
951 with tt.AssertPrints('SyntaxError: invalid syntax'):
951 with tt.AssertPrints('SyntaxError: invalid syntax'):
952 ip.run_cell('1 2 3') # plain python syntax error
952 ip.run_cell('1 2 3') # plain python syntax error
953 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
953 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
954 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
954 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
955 with tt.AssertPrints('3456'):
955 with tt.AssertPrints('3456'):
956 ip.run_cell('3456')
956 ip.run_cell('3456')
957
957
958
958
959 class TestWarningSuppression(unittest.TestCase):
959 class TestWarningSuppression(unittest.TestCase):
960 def test_warning_suppression(self):
960 def test_warning_suppression(self):
961 ip.run_cell("import warnings")
961 ip.run_cell("import warnings")
962 try:
962 try:
963 with self.assertWarnsRegex(UserWarning, "asdf"):
963 with self.assertWarnsRegex(UserWarning, "asdf"):
964 ip.run_cell("warnings.warn('asdf')")
964 ip.run_cell("warnings.warn('asdf')")
965 # Here's the real test -- if we run that again, we should get the
965 # Here's the real test -- if we run that again, we should get the
966 # warning again. Traditionally, each warning was only issued once per
966 # warning again. Traditionally, each warning was only issued once per
967 # IPython session (approximately), even if the user typed in new and
967 # IPython session (approximately), even if the user typed in new and
968 # different code that should have also triggered the warning, leading
968 # different code that should have also triggered the warning, leading
969 # to much confusion.
969 # to much confusion.
970 with self.assertWarnsRegex(UserWarning, "asdf"):
970 with self.assertWarnsRegex(UserWarning, "asdf"):
971 ip.run_cell("warnings.warn('asdf')")
971 ip.run_cell("warnings.warn('asdf')")
972 finally:
972 finally:
973 ip.run_cell("del warnings")
973 ip.run_cell("del warnings")
974
974
975
975
976 def test_deprecation_warning(self):
976 def test_deprecation_warning(self):
977 ip.run_cell("""
977 ip.run_cell("""
978 import warnings
978 import warnings
979 def wrn():
979 def wrn():
980 warnings.warn(
980 warnings.warn(
981 "I AM A WARNING",
981 "I AM A WARNING",
982 DeprecationWarning
982 DeprecationWarning
983 )
983 )
984 """)
984 """)
985 try:
985 try:
986 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
986 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
987 ip.run_cell("wrn()")
987 ip.run_cell("wrn()")
988 finally:
988 finally:
989 ip.run_cell("del warnings")
989 ip.run_cell("del warnings")
990 ip.run_cell("del wrn")
990 ip.run_cell("del wrn")
991
991
992
992
993 class TestImportNoDeprecate(tt.TempFileMixin):
993 class TestImportNoDeprecate(tt.TempFileMixin):
994
994
995 def setUp(self):
995 def setUp(self):
996 """Make a valid python temp file."""
996 """Make a valid python temp file."""
997 self.mktmp("""
997 self.mktmp("""
998 import warnings
998 import warnings
999 def wrn():
999 def wrn():
1000 warnings.warn(
1000 warnings.warn(
1001 "I AM A WARNING",
1001 "I AM A WARNING",
1002 DeprecationWarning
1002 DeprecationWarning
1003 )
1003 )
1004 """)
1004 """)
1005 super().setUp()
1005 super().setUp()
1006
1006
1007 def test_no_dep(self):
1007 def test_no_dep(self):
1008 """
1008 """
1009 No deprecation warning should be raised from imported functions
1009 No deprecation warning should be raised from imported functions
1010 """
1010 """
1011 ip.run_cell("from {} import wrn".format(self.fname))
1011 ip.run_cell("from {} import wrn".format(self.fname))
1012
1012
1013 with tt.AssertNotPrints("I AM A WARNING"):
1013 with tt.AssertNotPrints("I AM A WARNING"):
1014 ip.run_cell("wrn()")
1014 ip.run_cell("wrn()")
1015 ip.run_cell("del wrn")
1015 ip.run_cell("del wrn")
1016
1016
1017
1017
1018 def test_custom_exc_count():
1018 def test_custom_exc_count():
1019 hook = mock.Mock(return_value=None)
1019 hook = mock.Mock(return_value=None)
1020 ip.set_custom_exc((SyntaxError,), hook)
1020 ip.set_custom_exc((SyntaxError,), hook)
1021 before = ip.execution_count
1021 before = ip.execution_count
1022 ip.run_cell("def foo()", store_history=True)
1022 ip.run_cell("def foo()", store_history=True)
1023 # restore default excepthook
1023 # restore default excepthook
1024 ip.set_custom_exc((), None)
1024 ip.set_custom_exc((), None)
1025 nt.assert_equal(hook.call_count, 1)
1025 nt.assert_equal(hook.call_count, 1)
1026 nt.assert_equal(ip.execution_count, before + 1)
1026 nt.assert_equal(ip.execution_count, before + 1)
1027
1027
1028
1028
1029 def test_run_cell_async():
1029 def test_run_cell_async():
1030 loop = asyncio.get_event_loop()
1030 loop = asyncio.get_event_loop()
1031 ip.run_cell("import asyncio")
1031 ip.run_cell("import asyncio")
1032 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1032 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1033 assert asyncio.iscoroutine(coro)
1033 assert asyncio.iscoroutine(coro)
1034 result = loop.run_until_complete(coro)
1034 result = loop.run_until_complete(coro)
1035 assert isinstance(result, interactiveshell.ExecutionResult)
1035 assert isinstance(result, interactiveshell.ExecutionResult)
1036 assert result.result == 5
1036 assert result.result == 5
1037
1037
1038
1038
1039 def test_should_run_async():
1039 def test_should_run_async():
1040 assert not ip.should_run_async("a = 5")
1040 assert not ip.should_run_async("a = 5")
1041 assert ip.should_run_async("await x")
1041 assert ip.should_run_async("await x")
1042 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1042 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1043
1043
1044
1044
1045 def test_set_custom_completer():
1045 def test_set_custom_completer():
1046 num_completers = len(ip.Completer.matchers)
1046 num_completers = len(ip.Completer.matchers)
1047
1047
1048 def foo(*args, **kwargs):
1048 def foo(*args, **kwargs):
1049 return "I'm a completer!"
1049 return "I'm a completer!"
1050
1050
1051 ip.set_custom_completer(foo, 0)
1051 ip.set_custom_completer(foo, 0)
1052
1052
1053 # check that we've really added a new completer
1053 # check that we've really added a new completer
1054 assert len(ip.Completer.matchers) == num_completers + 1
1054 assert len(ip.Completer.matchers) == num_completers + 1
1055
1055
1056 # check that the first completer is the function we defined
1056 # check that the first completer is the function we defined
1057 assert ip.Completer.matchers[0]() == "I'm a completer!"
1057 assert ip.Completer.matchers[0]() == "I'm a completer!"
1058
1058
1059 # clean up
1059 # clean up
1060 ip.Completer.custom_matchers.pop()
1060 ip.Completer.custom_matchers.pop()
General Comments 0
You need to be logged in to leave comments. Login now