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