##// END OF EJS Templates
Fix Mixin Inheritence in Xunit unittest....
Matthias Bussonnier -
Show More
@@ -1,993 +1,1004 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 ip = get_ipython()
39 ip = get_ipython()
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Tests
42 # Tests
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 class DerivedInterrupt(KeyboardInterrupt):
45 class DerivedInterrupt(KeyboardInterrupt):
46 pass
46 pass
47
47
48 class InteractiveShellTestCase(unittest.TestCase):
48 class InteractiveShellTestCase(unittest.TestCase):
49 def test_naked_string_cells(self):
49 def test_naked_string_cells(self):
50 """Test that cells with only naked strings are fully executed"""
50 """Test that cells with only naked strings are fully executed"""
51 # First, single-line inputs
51 # First, single-line inputs
52 ip.run_cell('"a"\n')
52 ip.run_cell('"a"\n')
53 self.assertEqual(ip.user_ns['_'], 'a')
53 self.assertEqual(ip.user_ns['_'], 'a')
54 # And also multi-line cells
54 # And also multi-line cells
55 ip.run_cell('"""a\nb"""\n')
55 ip.run_cell('"""a\nb"""\n')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
57
57
58 def test_run_empty_cell(self):
58 def test_run_empty_cell(self):
59 """Just make sure we don't get a horrible error with a blank
59 """Just make sure we don't get a horrible error with a blank
60 cell of input. Yes, I did overlook that."""
60 cell of input. Yes, I did overlook that."""
61 old_xc = ip.execution_count
61 old_xc = ip.execution_count
62 res = ip.run_cell('')
62 res = ip.run_cell('')
63 self.assertEqual(ip.execution_count, old_xc)
63 self.assertEqual(ip.execution_count, old_xc)
64 self.assertEqual(res.execution_count, None)
64 self.assertEqual(res.execution_count, None)
65
65
66 def test_run_cell_multiline(self):
66 def test_run_cell_multiline(self):
67 """Multi-block, multi-line cells must execute correctly.
67 """Multi-block, multi-line cells must execute correctly.
68 """
68 """
69 src = '\n'.join(["x=1",
69 src = '\n'.join(["x=1",
70 "y=2",
70 "y=2",
71 "if 1:",
71 "if 1:",
72 " x += 1",
72 " x += 1",
73 " y += 1",])
73 " y += 1",])
74 res = ip.run_cell(src)
74 res = ip.run_cell(src)
75 self.assertEqual(ip.user_ns['x'], 2)
75 self.assertEqual(ip.user_ns['x'], 2)
76 self.assertEqual(ip.user_ns['y'], 3)
76 self.assertEqual(ip.user_ns['y'], 3)
77 self.assertEqual(res.success, True)
77 self.assertEqual(res.success, True)
78 self.assertEqual(res.result, None)
78 self.assertEqual(res.result, None)
79
79
80 def test_multiline_string_cells(self):
80 def test_multiline_string_cells(self):
81 "Code sprinkled with multiline strings should execute (GH-306)"
81 "Code sprinkled with multiline strings should execute (GH-306)"
82 ip.run_cell('tmp=0')
82 ip.run_cell('tmp=0')
83 self.assertEqual(ip.user_ns['tmp'], 0)
83 self.assertEqual(ip.user_ns['tmp'], 0)
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 self.assertEqual(ip.user_ns['tmp'], 1)
85 self.assertEqual(ip.user_ns['tmp'], 1)
86 self.assertEqual(res.success, True)
86 self.assertEqual(res.success, True)
87 self.assertEqual(res.result, "a\nb")
87 self.assertEqual(res.result, "a\nb")
88
88
89 def test_dont_cache_with_semicolon(self):
89 def test_dont_cache_with_semicolon(self):
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 oldlen = len(ip.user_ns['Out'])
91 oldlen = len(ip.user_ns['Out'])
92 for cell in ['1;', '1;1;']:
92 for cell in ['1;', '1;1;']:
93 res = ip.run_cell(cell, store_history=True)
93 res = ip.run_cell(cell, store_history=True)
94 newlen = len(ip.user_ns['Out'])
94 newlen = len(ip.user_ns['Out'])
95 self.assertEqual(oldlen, newlen)
95 self.assertEqual(oldlen, newlen)
96 self.assertIsNone(res.result)
96 self.assertIsNone(res.result)
97 i = 0
97 i = 0
98 #also test the default caching behavior
98 #also test the default caching behavior
99 for cell in ['1', '1;1']:
99 for cell in ['1', '1;1']:
100 ip.run_cell(cell, store_history=True)
100 ip.run_cell(cell, store_history=True)
101 newlen = len(ip.user_ns['Out'])
101 newlen = len(ip.user_ns['Out'])
102 i += 1
102 i += 1
103 self.assertEqual(oldlen+i, newlen)
103 self.assertEqual(oldlen+i, newlen)
104
104
105 def test_syntax_error(self):
105 def test_syntax_error(self):
106 res = ip.run_cell("raise = 3")
106 res = ip.run_cell("raise = 3")
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
108
108
109 def test_In_variable(self):
109 def test_In_variable(self):
110 "Verify that In variable grows with user input (GH-284)"
110 "Verify that In variable grows with user input (GH-284)"
111 oldlen = len(ip.user_ns['In'])
111 oldlen = len(ip.user_ns['In'])
112 ip.run_cell('1;', store_history=True)
112 ip.run_cell('1;', store_history=True)
113 newlen = len(ip.user_ns['In'])
113 newlen = len(ip.user_ns['In'])
114 self.assertEqual(oldlen+1, newlen)
114 self.assertEqual(oldlen+1, newlen)
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
116
116
117 def test_magic_names_in_string(self):
117 def test_magic_names_in_string(self):
118 ip.run_cell('a = """\n%exit\n"""')
118 ip.run_cell('a = """\n%exit\n"""')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120
120
121 def test_trailing_newline(self):
121 def test_trailing_newline(self):
122 """test that running !(command) does not raise a SyntaxError"""
122 """test that running !(command) does not raise a SyntaxError"""
123 ip.run_cell('!(true)\n', False)
123 ip.run_cell('!(true)\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
125
125
126 def test_gh_597(self):
126 def test_gh_597(self):
127 """Pretty-printing lists of objects with non-ascii reprs may cause
127 """Pretty-printing lists of objects with non-ascii reprs may cause
128 problems."""
128 problems."""
129 class Spam(object):
129 class Spam(object):
130 def __repr__(self):
130 def __repr__(self):
131 return "\xe9"*50
131 return "\xe9"*50
132 import IPython.core.formatters
132 import IPython.core.formatters
133 f = IPython.core.formatters.PlainTextFormatter()
133 f = IPython.core.formatters.PlainTextFormatter()
134 f([Spam(),Spam()])
134 f([Spam(),Spam()])
135
135
136
136
137 def test_future_flags(self):
137 def test_future_flags(self):
138 """Check that future flags are used for parsing code (gh-777)"""
138 """Check that future flags are used for parsing code (gh-777)"""
139 ip.run_cell('from __future__ import barry_as_FLUFL')
139 ip.run_cell('from __future__ import barry_as_FLUFL')
140 try:
140 try:
141 ip.run_cell('prfunc_return_val = 1 <> 2')
141 ip.run_cell('prfunc_return_val = 1 <> 2')
142 assert 'prfunc_return_val' in ip.user_ns
142 assert 'prfunc_return_val' in ip.user_ns
143 finally:
143 finally:
144 # Reset compiler flags so we don't mess up other tests.
144 # Reset compiler flags so we don't mess up other tests.
145 ip.compile.reset_compiler_flags()
145 ip.compile.reset_compiler_flags()
146
146
147 def test_can_pickle(self):
147 def test_can_pickle(self):
148 "Can we pickle objects defined interactively (GH-29)"
148 "Can we pickle objects defined interactively (GH-29)"
149 ip = get_ipython()
149 ip = get_ipython()
150 ip.reset()
150 ip.reset()
151 ip.run_cell(("class Mylist(list):\n"
151 ip.run_cell(("class Mylist(list):\n"
152 " def __init__(self,x=[]):\n"
152 " def __init__(self,x=[]):\n"
153 " list.__init__(self,x)"))
153 " list.__init__(self,x)"))
154 ip.run_cell("w=Mylist([1,2,3])")
154 ip.run_cell("w=Mylist([1,2,3])")
155
155
156 from pickle import dumps
156 from pickle import dumps
157
157
158 # We need to swap in our main module - this is only necessary
158 # We need to swap in our main module - this is only necessary
159 # inside the test framework, because IPython puts the interactive module
159 # inside the test framework, because IPython puts the interactive module
160 # in place (but the test framework undoes this).
160 # in place (but the test framework undoes this).
161 _main = sys.modules['__main__']
161 _main = sys.modules['__main__']
162 sys.modules['__main__'] = ip.user_module
162 sys.modules['__main__'] = ip.user_module
163 try:
163 try:
164 res = dumps(ip.user_ns["w"])
164 res = dumps(ip.user_ns["w"])
165 finally:
165 finally:
166 sys.modules['__main__'] = _main
166 sys.modules['__main__'] = _main
167 self.assertTrue(isinstance(res, bytes))
167 self.assertTrue(isinstance(res, bytes))
168
168
169 def test_global_ns(self):
169 def test_global_ns(self):
170 "Code in functions must be able to access variables outside them."
170 "Code in functions must be able to access variables outside them."
171 ip = get_ipython()
171 ip = get_ipython()
172 ip.run_cell("a = 10")
172 ip.run_cell("a = 10")
173 ip.run_cell(("def f(x):\n"
173 ip.run_cell(("def f(x):\n"
174 " return x + a"))
174 " return x + a"))
175 ip.run_cell("b = f(12)")
175 ip.run_cell("b = f(12)")
176 self.assertEqual(ip.user_ns["b"], 22)
176 self.assertEqual(ip.user_ns["b"], 22)
177
177
178 def test_bad_custom_tb(self):
178 def test_bad_custom_tb(self):
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 self.assertEqual(ip.custom_exceptions, (IOError,))
181 self.assertEqual(ip.custom_exceptions, (IOError,))
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 ip.run_cell(u'raise IOError("foo")')
183 ip.run_cell(u'raise IOError("foo")')
184 self.assertEqual(ip.custom_exceptions, ())
184 self.assertEqual(ip.custom_exceptions, ())
185
185
186 def test_bad_custom_tb_return(self):
186 def test_bad_custom_tb_return(self):
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 self.assertEqual(ip.custom_exceptions, (NameError,))
189 self.assertEqual(ip.custom_exceptions, (NameError,))
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 ip.run_cell(u'a=abracadabra')
191 ip.run_cell(u'a=abracadabra')
192 self.assertEqual(ip.custom_exceptions, ())
192 self.assertEqual(ip.custom_exceptions, ())
193
193
194 def test_drop_by_id(self):
194 def test_drop_by_id(self):
195 myvars = {"a":object(), "b":object(), "c": object()}
195 myvars = {"a":object(), "b":object(), "c": object()}
196 ip.push(myvars, interactive=False)
196 ip.push(myvars, interactive=False)
197 for name in myvars:
197 for name in myvars:
198 assert name in ip.user_ns, name
198 assert name in ip.user_ns, name
199 assert name in ip.user_ns_hidden, name
199 assert name in ip.user_ns_hidden, name
200 ip.user_ns['b'] = 12
200 ip.user_ns['b'] = 12
201 ip.drop_by_id(myvars)
201 ip.drop_by_id(myvars)
202 for name in ["a", "c"]:
202 for name in ["a", "c"]:
203 assert name not in ip.user_ns, name
203 assert name not in ip.user_ns, name
204 assert name not in ip.user_ns_hidden, name
204 assert name not in ip.user_ns_hidden, name
205 assert ip.user_ns['b'] == 12
205 assert ip.user_ns['b'] == 12
206 ip.reset()
206 ip.reset()
207
207
208 def test_var_expand(self):
208 def test_var_expand(self):
209 ip.user_ns['f'] = u'Ca\xf1o'
209 ip.user_ns['f'] = u'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}'), u'echo Ca\xf1o')
211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214
214
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216
216
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 # This should not raise any exception:
218 # This should not raise any exception:
219 ip.var_expand(u'echo $f')
219 ip.var_expand(u'echo $f')
220
220
221 def test_var_expand_local(self):
221 def test_var_expand_local(self):
222 """Test local variable expansion in !system and %magic calls"""
222 """Test local variable expansion in !system and %magic calls"""
223 # !system
223 # !system
224 ip.run_cell('def test():\n'
224 ip.run_cell('def test():\n'
225 ' lvar = "ttt"\n'
225 ' lvar = "ttt"\n'
226 ' ret = !echo {lvar}\n'
226 ' ret = !echo {lvar}\n'
227 ' return ret[0]\n')
227 ' return ret[0]\n')
228 res = ip.user_ns['test']()
228 res = ip.user_ns['test']()
229 nt.assert_in('ttt', res)
229 nt.assert_in('ttt', res)
230
230
231 # %magic
231 # %magic
232 ip.run_cell('def makemacro():\n'
232 ip.run_cell('def makemacro():\n'
233 ' macroname = "macro_var_expand_locals"\n'
233 ' macroname = "macro_var_expand_locals"\n'
234 ' %macro {macroname} codestr\n')
234 ' %macro {macroname} codestr\n')
235 ip.user_ns['codestr'] = "str(12)"
235 ip.user_ns['codestr'] = "str(12)"
236 ip.run_cell('makemacro()')
236 ip.run_cell('makemacro()')
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238
238
239 def test_var_expand_self(self):
239 def test_var_expand_self(self):
240 """Test variable expansion with the name 'self', which was failing.
240 """Test variable expansion with the name 'self', which was failing.
241
241
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 """
243 """
244 ip.run_cell('class cTest:\n'
244 ip.run_cell('class cTest:\n'
245 ' classvar="see me"\n'
245 ' classvar="see me"\n'
246 ' def test(self):\n'
246 ' def test(self):\n'
247 ' res = !echo Variable: {self.classvar}\n'
247 ' res = !echo Variable: {self.classvar}\n'
248 ' return res[0]\n')
248 ' return res[0]\n')
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
250
250
251 def test_bad_var_expand(self):
251 def test_bad_var_expand(self):
252 """var_expand on invalid formats shouldn't raise"""
252 """var_expand on invalid formats shouldn't raise"""
253 # SyntaxError
253 # SyntaxError
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 # NameError
255 # NameError
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 # ZeroDivisionError
257 # ZeroDivisionError
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259
259
260 def test_silent_postexec(self):
260 def test_silent_postexec(self):
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 pre_explicit = mock.Mock()
262 pre_explicit = mock.Mock()
263 pre_always = mock.Mock()
263 pre_always = mock.Mock()
264 post_explicit = mock.Mock()
264 post_explicit = mock.Mock()
265 post_always = mock.Mock()
265 post_always = mock.Mock()
266 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
266 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
267
267
268 ip.events.register('pre_run_cell', pre_explicit)
268 ip.events.register('pre_run_cell', pre_explicit)
269 ip.events.register('pre_execute', pre_always)
269 ip.events.register('pre_execute', pre_always)
270 ip.events.register('post_run_cell', post_explicit)
270 ip.events.register('post_run_cell', post_explicit)
271 ip.events.register('post_execute', post_always)
271 ip.events.register('post_execute', post_always)
272
272
273 try:
273 try:
274 ip.run_cell("1", silent=True)
274 ip.run_cell("1", silent=True)
275 assert pre_always.called
275 assert pre_always.called
276 assert not pre_explicit.called
276 assert not pre_explicit.called
277 assert post_always.called
277 assert post_always.called
278 assert not post_explicit.called
278 assert not post_explicit.called
279 # double-check that non-silent exec did what we expected
279 # double-check that non-silent exec did what we expected
280 # silent to avoid
280 # silent to avoid
281 ip.run_cell("1")
281 ip.run_cell("1")
282 assert pre_explicit.called
282 assert pre_explicit.called
283 assert post_explicit.called
283 assert post_explicit.called
284 info, = pre_explicit.call_args[0]
284 info, = pre_explicit.call_args[0]
285 result, = post_explicit.call_args[0]
285 result, = post_explicit.call_args[0]
286 self.assertEqual(info, result.info)
286 self.assertEqual(info, result.info)
287 # check that post hooks are always called
287 # check that post hooks are always called
288 [m.reset_mock() for m in all_mocks]
288 [m.reset_mock() for m in all_mocks]
289 ip.run_cell("syntax error")
289 ip.run_cell("syntax error")
290 assert pre_always.called
290 assert pre_always.called
291 assert pre_explicit.called
291 assert pre_explicit.called
292 assert post_always.called
292 assert post_always.called
293 assert post_explicit.called
293 assert post_explicit.called
294 info, = pre_explicit.call_args[0]
294 info, = pre_explicit.call_args[0]
295 result, = post_explicit.call_args[0]
295 result, = post_explicit.call_args[0]
296 self.assertEqual(info, result.info)
296 self.assertEqual(info, result.info)
297 finally:
297 finally:
298 # remove post-exec
298 # remove post-exec
299 ip.events.unregister('pre_run_cell', pre_explicit)
299 ip.events.unregister('pre_run_cell', pre_explicit)
300 ip.events.unregister('pre_execute', pre_always)
300 ip.events.unregister('pre_execute', pre_always)
301 ip.events.unregister('post_run_cell', post_explicit)
301 ip.events.unregister('post_run_cell', post_explicit)
302 ip.events.unregister('post_execute', post_always)
302 ip.events.unregister('post_execute', post_always)
303
303
304 def test_silent_noadvance(self):
304 def test_silent_noadvance(self):
305 """run_cell(silent=True) doesn't advance execution_count"""
305 """run_cell(silent=True) doesn't advance execution_count"""
306 ec = ip.execution_count
306 ec = ip.execution_count
307 # silent should force store_history=False
307 # silent should force store_history=False
308 ip.run_cell("1", store_history=True, silent=True)
308 ip.run_cell("1", store_history=True, silent=True)
309
309
310 self.assertEqual(ec, ip.execution_count)
310 self.assertEqual(ec, ip.execution_count)
311 # double-check that non-silent exec did what we expected
311 # double-check that non-silent exec did what we expected
312 # silent to avoid
312 # silent to avoid
313 ip.run_cell("1", store_history=True)
313 ip.run_cell("1", store_history=True)
314 self.assertEqual(ec+1, ip.execution_count)
314 self.assertEqual(ec+1, ip.execution_count)
315
315
316 def test_silent_nodisplayhook(self):
316 def test_silent_nodisplayhook(self):
317 """run_cell(silent=True) doesn't trigger displayhook"""
317 """run_cell(silent=True) doesn't trigger displayhook"""
318 d = dict(called=False)
318 d = dict(called=False)
319
319
320 trap = ip.display_trap
320 trap = ip.display_trap
321 save_hook = trap.hook
321 save_hook = trap.hook
322
322
323 def failing_hook(*args, **kwargs):
323 def failing_hook(*args, **kwargs):
324 d['called'] = True
324 d['called'] = True
325
325
326 try:
326 try:
327 trap.hook = failing_hook
327 trap.hook = failing_hook
328 res = ip.run_cell("1", silent=True)
328 res = ip.run_cell("1", silent=True)
329 self.assertFalse(d['called'])
329 self.assertFalse(d['called'])
330 self.assertIsNone(res.result)
330 self.assertIsNone(res.result)
331 # double-check that non-silent exec did what we expected
331 # double-check that non-silent exec did what we expected
332 # silent to avoid
332 # silent to avoid
333 ip.run_cell("1")
333 ip.run_cell("1")
334 self.assertTrue(d['called'])
334 self.assertTrue(d['called'])
335 finally:
335 finally:
336 trap.hook = save_hook
336 trap.hook = save_hook
337
337
338 def test_ofind_line_magic(self):
338 def test_ofind_line_magic(self):
339 from IPython.core.magic import register_line_magic
339 from IPython.core.magic import register_line_magic
340
340
341 @register_line_magic
341 @register_line_magic
342 def lmagic(line):
342 def lmagic(line):
343 "A line magic"
343 "A line magic"
344
344
345 # Get info on line magic
345 # Get info on line magic
346 lfind = ip._ofind('lmagic')
346 lfind = ip._ofind('lmagic')
347 info = dict(found=True, isalias=False, ismagic=True,
347 info = dict(found=True, isalias=False, ismagic=True,
348 namespace = 'IPython internal', obj= lmagic.__wrapped__,
348 namespace = 'IPython internal', obj= lmagic.__wrapped__,
349 parent = None)
349 parent = None)
350 nt.assert_equal(lfind, info)
350 nt.assert_equal(lfind, info)
351
351
352 def test_ofind_cell_magic(self):
352 def test_ofind_cell_magic(self):
353 from IPython.core.magic import register_cell_magic
353 from IPython.core.magic import register_cell_magic
354
354
355 @register_cell_magic
355 @register_cell_magic
356 def cmagic(line, cell):
356 def cmagic(line, cell):
357 "A cell magic"
357 "A cell magic"
358
358
359 # Get info on cell magic
359 # Get info on cell magic
360 find = ip._ofind('cmagic')
360 find = ip._ofind('cmagic')
361 info = dict(found=True, isalias=False, ismagic=True,
361 info = dict(found=True, isalias=False, ismagic=True,
362 namespace = 'IPython internal', obj= cmagic.__wrapped__,
362 namespace = 'IPython internal', obj= cmagic.__wrapped__,
363 parent = None)
363 parent = None)
364 nt.assert_equal(find, info)
364 nt.assert_equal(find, info)
365
365
366 def test_ofind_property_with_error(self):
366 def test_ofind_property_with_error(self):
367 class A(object):
367 class A(object):
368 @property
368 @property
369 def foo(self):
369 def foo(self):
370 raise NotImplementedError()
370 raise NotImplementedError()
371 a = A()
371 a = A()
372
372
373 found = ip._ofind('a.foo', [('locals', locals())])
373 found = ip._ofind('a.foo', [('locals', locals())])
374 info = dict(found=True, isalias=False, ismagic=False,
374 info = dict(found=True, isalias=False, ismagic=False,
375 namespace='locals', obj=A.foo, parent=a)
375 namespace='locals', obj=A.foo, parent=a)
376 nt.assert_equal(found, info)
376 nt.assert_equal(found, info)
377
377
378 def test_ofind_multiple_attribute_lookups(self):
378 def test_ofind_multiple_attribute_lookups(self):
379 class A(object):
379 class A(object):
380 @property
380 @property
381 def foo(self):
381 def foo(self):
382 raise NotImplementedError()
382 raise NotImplementedError()
383
383
384 a = A()
384 a = A()
385 a.a = A()
385 a.a = A()
386 a.a.a = A()
386 a.a.a = A()
387
387
388 found = ip._ofind('a.a.a.foo', [('locals', locals())])
388 found = ip._ofind('a.a.a.foo', [('locals', locals())])
389 info = dict(found=True, isalias=False, ismagic=False,
389 info = dict(found=True, isalias=False, ismagic=False,
390 namespace='locals', obj=A.foo, parent=a.a.a)
390 namespace='locals', obj=A.foo, parent=a.a.a)
391 nt.assert_equal(found, info)
391 nt.assert_equal(found, info)
392
392
393 def test_ofind_slotted_attributes(self):
393 def test_ofind_slotted_attributes(self):
394 class A(object):
394 class A(object):
395 __slots__ = ['foo']
395 __slots__ = ['foo']
396 def __init__(self):
396 def __init__(self):
397 self.foo = 'bar'
397 self.foo = 'bar'
398
398
399 a = A()
399 a = A()
400 found = ip._ofind('a.foo', [('locals', locals())])
400 found = ip._ofind('a.foo', [('locals', locals())])
401 info = dict(found=True, isalias=False, ismagic=False,
401 info = dict(found=True, isalias=False, ismagic=False,
402 namespace='locals', obj=a.foo, parent=a)
402 namespace='locals', obj=a.foo, parent=a)
403 nt.assert_equal(found, info)
403 nt.assert_equal(found, info)
404
404
405 found = ip._ofind('a.bar', [('locals', locals())])
405 found = ip._ofind('a.bar', [('locals', locals())])
406 info = dict(found=False, isalias=False, ismagic=False,
406 info = dict(found=False, isalias=False, ismagic=False,
407 namespace=None, obj=None, parent=a)
407 namespace=None, obj=None, parent=a)
408 nt.assert_equal(found, info)
408 nt.assert_equal(found, info)
409
409
410 def test_ofind_prefers_property_to_instance_level_attribute(self):
410 def test_ofind_prefers_property_to_instance_level_attribute(self):
411 class A(object):
411 class A(object):
412 @property
412 @property
413 def foo(self):
413 def foo(self):
414 return 'bar'
414 return 'bar'
415 a = A()
415 a = A()
416 a.__dict__['foo'] = 'baz'
416 a.__dict__['foo'] = 'baz'
417 nt.assert_equal(a.foo, 'bar')
417 nt.assert_equal(a.foo, 'bar')
418 found = ip._ofind('a.foo', [('locals', locals())])
418 found = ip._ofind('a.foo', [('locals', locals())])
419 nt.assert_is(found['obj'], A.foo)
419 nt.assert_is(found['obj'], A.foo)
420
420
421 def test_custom_syntaxerror_exception(self):
421 def test_custom_syntaxerror_exception(self):
422 called = []
422 called = []
423 def my_handler(shell, etype, value, tb, tb_offset=None):
423 def my_handler(shell, etype, value, tb, tb_offset=None):
424 called.append(etype)
424 called.append(etype)
425 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
425 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
426
426
427 ip.set_custom_exc((SyntaxError,), my_handler)
427 ip.set_custom_exc((SyntaxError,), my_handler)
428 try:
428 try:
429 ip.run_cell("1f")
429 ip.run_cell("1f")
430 # Check that this was called, and only once.
430 # Check that this was called, and only once.
431 self.assertEqual(called, [SyntaxError])
431 self.assertEqual(called, [SyntaxError])
432 finally:
432 finally:
433 # Reset the custom exception hook
433 # Reset the custom exception hook
434 ip.set_custom_exc((), None)
434 ip.set_custom_exc((), None)
435
435
436 def test_custom_exception(self):
436 def test_custom_exception(self):
437 called = []
437 called = []
438 def my_handler(shell, etype, value, tb, tb_offset=None):
438 def my_handler(shell, etype, value, tb, tb_offset=None):
439 called.append(etype)
439 called.append(etype)
440 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
440 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
441
441
442 ip.set_custom_exc((ValueError,), my_handler)
442 ip.set_custom_exc((ValueError,), my_handler)
443 try:
443 try:
444 res = ip.run_cell("raise ValueError('test')")
444 res = ip.run_cell("raise ValueError('test')")
445 # Check that this was called, and only once.
445 # Check that this was called, and only once.
446 self.assertEqual(called, [ValueError])
446 self.assertEqual(called, [ValueError])
447 # Check that the error is on the result object
447 # Check that the error is on the result object
448 self.assertIsInstance(res.error_in_exec, ValueError)
448 self.assertIsInstance(res.error_in_exec, ValueError)
449 finally:
449 finally:
450 # Reset the custom exception hook
450 # Reset the custom exception hook
451 ip.set_custom_exc((), None)
451 ip.set_custom_exc((), None)
452
452
453 def test_mktempfile(self):
453 def test_mktempfile(self):
454 filename = ip.mktempfile()
454 filename = ip.mktempfile()
455 # Check that we can open the file again on Windows
455 # Check that we can open the file again on Windows
456 with open(filename, 'w') as f:
456 with open(filename, 'w') as f:
457 f.write('abc')
457 f.write('abc')
458
458
459 filename = ip.mktempfile(data='blah')
459 filename = ip.mktempfile(data='blah')
460 with open(filename, 'r') as f:
460 with open(filename, 'r') as f:
461 self.assertEqual(f.read(), 'blah')
461 self.assertEqual(f.read(), 'blah')
462
462
463 def test_new_main_mod(self):
463 def test_new_main_mod(self):
464 # Smoketest to check that this accepts a unicode module name
464 # Smoketest to check that this accepts a unicode module name
465 name = u'jiefmw'
465 name = u'jiefmw'
466 mod = ip.new_main_mod(u'%s.py' % name, name)
466 mod = ip.new_main_mod(u'%s.py' % name, name)
467 self.assertEqual(mod.__name__, name)
467 self.assertEqual(mod.__name__, name)
468
468
469 def test_get_exception_only(self):
469 def test_get_exception_only(self):
470 try:
470 try:
471 raise KeyboardInterrupt
471 raise KeyboardInterrupt
472 except KeyboardInterrupt:
472 except KeyboardInterrupt:
473 msg = ip.get_exception_only()
473 msg = ip.get_exception_only()
474 self.assertEqual(msg, 'KeyboardInterrupt\n')
474 self.assertEqual(msg, 'KeyboardInterrupt\n')
475
475
476 try:
476 try:
477 raise DerivedInterrupt("foo")
477 raise DerivedInterrupt("foo")
478 except KeyboardInterrupt:
478 except KeyboardInterrupt:
479 msg = ip.get_exception_only()
479 msg = ip.get_exception_only()
480 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
480 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
481
481
482 def test_inspect_text(self):
482 def test_inspect_text(self):
483 ip.run_cell('a = 5')
483 ip.run_cell('a = 5')
484 text = ip.object_inspect_text('a')
484 text = ip.object_inspect_text('a')
485 self.assertIsInstance(text, str)
485 self.assertIsInstance(text, str)
486
486
487 def test_last_execution_result(self):
487 def test_last_execution_result(self):
488 """ Check that last execution result gets set correctly (GH-10702) """
488 """ Check that last execution result gets set correctly (GH-10702) """
489 result = ip.run_cell('a = 5; a')
489 result = ip.run_cell('a = 5; a')
490 self.assertTrue(ip.last_execution_succeeded)
490 self.assertTrue(ip.last_execution_succeeded)
491 self.assertEqual(ip.last_execution_result.result, 5)
491 self.assertEqual(ip.last_execution_result.result, 5)
492
492
493 result = ip.run_cell('a = x_invalid_id_x')
493 result = ip.run_cell('a = x_invalid_id_x')
494 self.assertFalse(ip.last_execution_succeeded)
494 self.assertFalse(ip.last_execution_succeeded)
495 self.assertFalse(ip.last_execution_result.success)
495 self.assertFalse(ip.last_execution_result.success)
496 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
496 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
497
497
498 def test_reset_aliasing(self):
498 def test_reset_aliasing(self):
499 """ Check that standard posix aliases work after %reset. """
499 """ Check that standard posix aliases work after %reset. """
500 if os.name != 'posix':
500 if os.name != 'posix':
501 return
501 return
502
502
503 ip.reset()
503 ip.reset()
504 for cmd in ('clear', 'more', 'less', 'man'):
504 for cmd in ('clear', 'more', 'less', 'man'):
505 res = ip.run_cell('%' + cmd)
505 res = ip.run_cell('%' + cmd)
506 self.assertEqual(res.success, True)
506 self.assertEqual(res.success, True)
507
507
508
508
509 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
509 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
510
510
511 @onlyif_unicode_paths
511 @onlyif_unicode_paths
512 def setUp(self):
512 def setUp(self):
513 self.BASETESTDIR = tempfile.mkdtemp()
513 self.BASETESTDIR = tempfile.mkdtemp()
514 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
514 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
515 os.mkdir(self.TESTDIR)
515 os.mkdir(self.TESTDIR)
516 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
516 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
517 sfile.write("pass\n")
517 sfile.write("pass\n")
518 self.oldpath = os.getcwd()
518 self.oldpath = os.getcwd()
519 os.chdir(self.TESTDIR)
519 os.chdir(self.TESTDIR)
520 self.fname = u"Γ₯Àâtestscript.py"
520 self.fname = u"Γ₯Àâtestscript.py"
521
521
522 def tearDown(self):
522 def tearDown(self):
523 os.chdir(self.oldpath)
523 os.chdir(self.oldpath)
524 shutil.rmtree(self.BASETESTDIR)
524 shutil.rmtree(self.BASETESTDIR)
525
525
526 @onlyif_unicode_paths
526 @onlyif_unicode_paths
527 def test_1(self):
527 def test_1(self):
528 """Test safe_execfile with non-ascii path
528 """Test safe_execfile with non-ascii path
529 """
529 """
530 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
530 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
531
531
532 class ExitCodeChecks(tt.TempFileMixin):
532 class ExitCodeChecks(tt.TempFileMixin):
533
534 def setUp(self):
535 self.system = ip.system_raw
536
533 def test_exit_code_ok(self):
537 def test_exit_code_ok(self):
534 self.system('exit 0')
538 self.system('exit 0')
535 self.assertEqual(ip.user_ns['_exit_code'], 0)
539 self.assertEqual(ip.user_ns['_exit_code'], 0)
536
540
537 def test_exit_code_error(self):
541 def test_exit_code_error(self):
538 self.system('exit 1')
542 self.system('exit 1')
539 self.assertEqual(ip.user_ns['_exit_code'], 1)
543 self.assertEqual(ip.user_ns['_exit_code'], 1)
540
544
541 @skipif(not hasattr(signal, 'SIGALRM'))
545 @skipif(not hasattr(signal, 'SIGALRM'))
542 def test_exit_code_signal(self):
546 def test_exit_code_signal(self):
543 self.mktmp("import signal, time\n"
547 self.mktmp("import signal, time\n"
544 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
548 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
545 "time.sleep(1)\n")
549 "time.sleep(1)\n")
546 self.system("%s %s" % (sys.executable, self.fname))
550 self.system("%s %s" % (sys.executable, self.fname))
547 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
551 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
548
552
549 @onlyif_cmds_exist("csh")
553 @onlyif_cmds_exist("csh")
550 def test_exit_code_signal_csh(self):
554 def test_exit_code_signal_csh(self):
551 SHELL = os.environ.get('SHELL', None)
555 SHELL = os.environ.get('SHELL', None)
552 os.environ['SHELL'] = find_cmd("csh")
556 os.environ['SHELL'] = find_cmd("csh")
553 try:
557 try:
554 self.test_exit_code_signal()
558 self.test_exit_code_signal()
555 finally:
559 finally:
556 if SHELL is not None:
560 if SHELL is not None:
557 os.environ['SHELL'] = SHELL
561 os.environ['SHELL'] = SHELL
558 else:
562 else:
559 del os.environ['SHELL']
563 del os.environ['SHELL']
560
564
561
565
562 class TestSystemRaw(ExitCodeChecks, unittest.TestCase):
566 class TestSystemRaw(ExitCodeChecks):
563 system = ip.system_raw
567
568 def setUp(self):
569 super().setUp()
570 self.sytem = ip.system_raw
564
571
565 @onlyif_unicode_paths
572 @onlyif_unicode_paths
566 def test_1(self):
573 def test_1(self):
567 """Test system_raw with non-ascii cmd
574 """Test system_raw with non-ascii cmd
568 """
575 """
569 cmd = u'''python -c "'Γ₯Àâ'" '''
576 cmd = u'''python -c "'Γ₯Àâ'" '''
570 ip.system_raw(cmd)
577 ip.system_raw(cmd)
571
578
572 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
579 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
573 @mock.patch('os.system', side_effect=KeyboardInterrupt)
580 @mock.patch('os.system', side_effect=KeyboardInterrupt)
574 def test_control_c(self, *mocks):
581 def test_control_c(self, *mocks):
575 try:
582 try:
576 self.system("sleep 1 # wont happen")
583 self.system("sleep 1 # wont happen")
577 except KeyboardInterrupt:
584 except KeyboardInterrupt:
578 self.fail("system call should intercept "
585 self.fail("system call should intercept "
579 "keyboard interrupt from subprocess.call")
586 "keyboard interrupt from subprocess.call")
580 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
587 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
581
588
582 # TODO: Exit codes are currently ignored on Windows.
589 # TODO: Exit codes are currently ignored on Windows.
583 class TestSystemPipedExitCode(ExitCodeChecks, unittest.TestCase):
590 class TestSystemPipedExitCode(ExitCodeChecks):
584 system = ip.system_piped
591
592 def setUp(self):
593 super().setUp()
594 self.sytem = ip.system_piped
585
595
586 @skip_win32
596 @skip_win32
587 def test_exit_code_ok(self):
597 def test_exit_code_ok(self):
588 ExitCodeChecks.test_exit_code_ok(self)
598 ExitCodeChecks.test_exit_code_ok(self)
589
599
590 @skip_win32
600 @skip_win32
591 def test_exit_code_error(self):
601 def test_exit_code_error(self):
592 ExitCodeChecks.test_exit_code_error(self)
602 ExitCodeChecks.test_exit_code_error(self)
593
603
594 @skip_win32
604 @skip_win32
595 def test_exit_code_signal(self):
605 def test_exit_code_signal(self):
596 ExitCodeChecks.test_exit_code_signal(self)
606 ExitCodeChecks.test_exit_code_signal(self)
597
607
598 class TestModules(tt.TempFileMixin, unittest.TestCase):
608 class TestModules(tt.TempFileMixin):
599 def test_extraneous_loads(self):
609 def test_extraneous_loads(self):
600 """Test we're not loading modules on startup that we shouldn't.
610 """Test we're not loading modules on startup that we shouldn't.
601 """
611 """
602 self.mktmp("import sys\n"
612 self.mktmp("import sys\n"
603 "print('numpy' in sys.modules)\n"
613 "print('numpy' in sys.modules)\n"
604 "print('ipyparallel' in sys.modules)\n"
614 "print('ipyparallel' in sys.modules)\n"
605 "print('ipykernel' in sys.modules)\n"
615 "print('ipykernel' in sys.modules)\n"
606 )
616 )
607 out = "False\nFalse\nFalse\n"
617 out = "False\nFalse\nFalse\n"
608 tt.ipexec_validate(self.fname, out)
618 tt.ipexec_validate(self.fname, out)
609
619
610 class Negator(ast.NodeTransformer):
620 class Negator(ast.NodeTransformer):
611 """Negates all number literals in an AST."""
621 """Negates all number literals in an AST."""
612
622
613 # for python 3.7 and earlier
623 # for python 3.7 and earlier
614 def visit_Num(self, node):
624 def visit_Num(self, node):
615 node.n = -node.n
625 node.n = -node.n
616 return node
626 return node
617
627
618 # for python 3.8+
628 # for python 3.8+
619 def visit_Constant(self, node):
629 def visit_Constant(self, node):
620 if isinstance(node.value, int):
630 if isinstance(node.value, int):
621 return self.visit_Num(node)
631 return self.visit_Num(node)
622 return node
632 return node
623
633
624 class TestAstTransform(unittest.TestCase):
634 class TestAstTransform(unittest.TestCase):
625 def setUp(self):
635 def setUp(self):
626 self.negator = Negator()
636 self.negator = Negator()
627 ip.ast_transformers.append(self.negator)
637 ip.ast_transformers.append(self.negator)
628
638
629 def tearDown(self):
639 def tearDown(self):
630 ip.ast_transformers.remove(self.negator)
640 ip.ast_transformers.remove(self.negator)
631
641
632 def test_run_cell(self):
642 def test_run_cell(self):
633 with tt.AssertPrints('-34'):
643 with tt.AssertPrints('-34'):
634 ip.run_cell('print (12 + 22)')
644 ip.run_cell('print (12 + 22)')
635
645
636 # A named reference to a number shouldn't be transformed.
646 # A named reference to a number shouldn't be transformed.
637 ip.user_ns['n'] = 55
647 ip.user_ns['n'] = 55
638 with tt.AssertNotPrints('-55'):
648 with tt.AssertNotPrints('-55'):
639 ip.run_cell('print (n)')
649 ip.run_cell('print (n)')
640
650
641 def test_timeit(self):
651 def test_timeit(self):
642 called = set()
652 called = set()
643 def f(x):
653 def f(x):
644 called.add(x)
654 called.add(x)
645 ip.push({'f':f})
655 ip.push({'f':f})
646
656
647 with tt.AssertPrints("std. dev. of"):
657 with tt.AssertPrints("std. dev. of"):
648 ip.run_line_magic("timeit", "-n1 f(1)")
658 ip.run_line_magic("timeit", "-n1 f(1)")
649 self.assertEqual(called, {-1})
659 self.assertEqual(called, {-1})
650 called.clear()
660 called.clear()
651
661
652 with tt.AssertPrints("std. dev. of"):
662 with tt.AssertPrints("std. dev. of"):
653 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
663 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
654 self.assertEqual(called, {-2, -3})
664 self.assertEqual(called, {-2, -3})
655
665
656 def test_time(self):
666 def test_time(self):
657 called = []
667 called = []
658 def f(x):
668 def f(x):
659 called.append(x)
669 called.append(x)
660 ip.push({'f':f})
670 ip.push({'f':f})
661
671
662 # Test with an expression
672 # Test with an expression
663 with tt.AssertPrints("Wall time: "):
673 with tt.AssertPrints("Wall time: "):
664 ip.run_line_magic("time", "f(5+9)")
674 ip.run_line_magic("time", "f(5+9)")
665 self.assertEqual(called, [-14])
675 self.assertEqual(called, [-14])
666 called[:] = []
676 called[:] = []
667
677
668 # Test with a statement (different code path)
678 # Test with a statement (different code path)
669 with tt.AssertPrints("Wall time: "):
679 with tt.AssertPrints("Wall time: "):
670 ip.run_line_magic("time", "a = f(-3 + -2)")
680 ip.run_line_magic("time", "a = f(-3 + -2)")
671 self.assertEqual(called, [5])
681 self.assertEqual(called, [5])
672
682
673 def test_macro(self):
683 def test_macro(self):
674 ip.push({'a':10})
684 ip.push({'a':10})
675 # The AST transformation makes this do a+=-1
685 # The AST transformation makes this do a+=-1
676 ip.define_macro("amacro", "a+=1\nprint(a)")
686 ip.define_macro("amacro", "a+=1\nprint(a)")
677
687
678 with tt.AssertPrints("9"):
688 with tt.AssertPrints("9"):
679 ip.run_cell("amacro")
689 ip.run_cell("amacro")
680 with tt.AssertPrints("8"):
690 with tt.AssertPrints("8"):
681 ip.run_cell("amacro")
691 ip.run_cell("amacro")
682
692
683 class IntegerWrapper(ast.NodeTransformer):
693 class IntegerWrapper(ast.NodeTransformer):
684 """Wraps all integers in a call to Integer()"""
694 """Wraps all integers in a call to Integer()"""
685
695
686 # for Python 3.7 and earlier
696 # for Python 3.7 and earlier
687
697
688 # for Python 3.7 and earlier
698 # for Python 3.7 and earlier
689 def visit_Num(self, node):
699 def visit_Num(self, node):
690 if isinstance(node.n, int):
700 if isinstance(node.n, int):
691 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
701 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
692 args=[node], keywords=[])
702 args=[node], keywords=[])
693 return node
703 return node
694
704
695 # For Python 3.8+
705 # For Python 3.8+
696 def visit_Constant(self, node):
706 def visit_Constant(self, node):
697 if isinstance(node.value, int):
707 if isinstance(node.value, int):
698 return self.visit_Num(node)
708 return self.visit_Num(node)
699 return node
709 return node
700
710
701
711
702 class TestAstTransform2(unittest.TestCase):
712 class TestAstTransform2(unittest.TestCase):
703 def setUp(self):
713 def setUp(self):
704 self.intwrapper = IntegerWrapper()
714 self.intwrapper = IntegerWrapper()
705 ip.ast_transformers.append(self.intwrapper)
715 ip.ast_transformers.append(self.intwrapper)
706
716
707 self.calls = []
717 self.calls = []
708 def Integer(*args):
718 def Integer(*args):
709 self.calls.append(args)
719 self.calls.append(args)
710 return args
720 return args
711 ip.push({"Integer": Integer})
721 ip.push({"Integer": Integer})
712
722
713 def tearDown(self):
723 def tearDown(self):
714 ip.ast_transformers.remove(self.intwrapper)
724 ip.ast_transformers.remove(self.intwrapper)
715 del ip.user_ns['Integer']
725 del ip.user_ns['Integer']
716
726
717 def test_run_cell(self):
727 def test_run_cell(self):
718 ip.run_cell("n = 2")
728 ip.run_cell("n = 2")
719 self.assertEqual(self.calls, [(2,)])
729 self.assertEqual(self.calls, [(2,)])
720
730
721 # This shouldn't throw an error
731 # This shouldn't throw an error
722 ip.run_cell("o = 2.0")
732 ip.run_cell("o = 2.0")
723 self.assertEqual(ip.user_ns['o'], 2.0)
733 self.assertEqual(ip.user_ns['o'], 2.0)
724
734
725 def test_timeit(self):
735 def test_timeit(self):
726 called = set()
736 called = set()
727 def f(x):
737 def f(x):
728 called.add(x)
738 called.add(x)
729 ip.push({'f':f})
739 ip.push({'f':f})
730
740
731 with tt.AssertPrints("std. dev. of"):
741 with tt.AssertPrints("std. dev. of"):
732 ip.run_line_magic("timeit", "-n1 f(1)")
742 ip.run_line_magic("timeit", "-n1 f(1)")
733 self.assertEqual(called, {(1,)})
743 self.assertEqual(called, {(1,)})
734 called.clear()
744 called.clear()
735
745
736 with tt.AssertPrints("std. dev. of"):
746 with tt.AssertPrints("std. dev. of"):
737 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
747 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
738 self.assertEqual(called, {(2,), (3,)})
748 self.assertEqual(called, {(2,), (3,)})
739
749
740 class ErrorTransformer(ast.NodeTransformer):
750 class ErrorTransformer(ast.NodeTransformer):
741 """Throws an error when it sees a number."""
751 """Throws an error when it sees a number."""
742
752
743 # for Python 3.7 and earlier
753 # for Python 3.7 and earlier
744 def visit_Num(self, node):
754 def visit_Num(self, node):
745 raise ValueError("test")
755 raise ValueError("test")
746
756
747 # for Python 3.8+
757 # for Python 3.8+
748 def visit_Constant(self, node):
758 def visit_Constant(self, node):
749 if isinstance(node.value, int):
759 if isinstance(node.value, int):
750 return self.visit_Num(node)
760 return self.visit_Num(node)
751 return node
761 return node
752
762
753
763
754 class TestAstTransformError(unittest.TestCase):
764 class TestAstTransformError(unittest.TestCase):
755 def test_unregistering(self):
765 def test_unregistering(self):
756 err_transformer = ErrorTransformer()
766 err_transformer = ErrorTransformer()
757 ip.ast_transformers.append(err_transformer)
767 ip.ast_transformers.append(err_transformer)
758
768
759 with tt.AssertPrints("unregister", channel='stderr'):
769 with tt.AssertPrints("unregister", channel='stderr'):
760 ip.run_cell("1 + 2")
770 ip.run_cell("1 + 2")
761
771
762 # This should have been removed.
772 # This should have been removed.
763 nt.assert_not_in(err_transformer, ip.ast_transformers)
773 nt.assert_not_in(err_transformer, ip.ast_transformers)
764
774
765
775
766 class StringRejector(ast.NodeTransformer):
776 class StringRejector(ast.NodeTransformer):
767 """Throws an InputRejected when it sees a string literal.
777 """Throws an InputRejected when it sees a string literal.
768
778
769 Used to verify that NodeTransformers can signal that a piece of code should
779 Used to verify that NodeTransformers can signal that a piece of code should
770 not be executed by throwing an InputRejected.
780 not be executed by throwing an InputRejected.
771 """
781 """
772
782
773 #for python 3.7 and earlier
783 #for python 3.7 and earlier
774 def visit_Str(self, node):
784 def visit_Str(self, node):
775 raise InputRejected("test")
785 raise InputRejected("test")
776
786
777 # 3.8 only
787 # 3.8 only
778 def visit_Constant(self, node):
788 def visit_Constant(self, node):
779 if isinstance(node.value, str):
789 if isinstance(node.value, str):
780 raise InputRejected("test")
790 raise InputRejected("test")
781 return node
791 return node
782
792
783
793
784 class TestAstTransformInputRejection(unittest.TestCase):
794 class TestAstTransformInputRejection(unittest.TestCase):
785
795
786 def setUp(self):
796 def setUp(self):
787 self.transformer = StringRejector()
797 self.transformer = StringRejector()
788 ip.ast_transformers.append(self.transformer)
798 ip.ast_transformers.append(self.transformer)
789
799
790 def tearDown(self):
800 def tearDown(self):
791 ip.ast_transformers.remove(self.transformer)
801 ip.ast_transformers.remove(self.transformer)
792
802
793 def test_input_rejection(self):
803 def test_input_rejection(self):
794 """Check that NodeTransformers can reject input."""
804 """Check that NodeTransformers can reject input."""
795
805
796 expect_exception_tb = tt.AssertPrints("InputRejected: test")
806 expect_exception_tb = tt.AssertPrints("InputRejected: test")
797 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
807 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
798
808
799 # Run the same check twice to verify that the transformer is not
809 # Run the same check twice to verify that the transformer is not
800 # disabled after raising.
810 # disabled after raising.
801 with expect_exception_tb, expect_no_cell_output:
811 with expect_exception_tb, expect_no_cell_output:
802 ip.run_cell("'unsafe'")
812 ip.run_cell("'unsafe'")
803
813
804 with expect_exception_tb, expect_no_cell_output:
814 with expect_exception_tb, expect_no_cell_output:
805 res = ip.run_cell("'unsafe'")
815 res = ip.run_cell("'unsafe'")
806
816
807 self.assertIsInstance(res.error_before_exec, InputRejected)
817 self.assertIsInstance(res.error_before_exec, InputRejected)
808
818
809 def test__IPYTHON__():
819 def test__IPYTHON__():
810 # This shouldn't raise a NameError, that's all
820 # This shouldn't raise a NameError, that's all
811 __IPYTHON__
821 __IPYTHON__
812
822
813
823
814 class DummyRepr(object):
824 class DummyRepr(object):
815 def __repr__(self):
825 def __repr__(self):
816 return "DummyRepr"
826 return "DummyRepr"
817
827
818 def _repr_html_(self):
828 def _repr_html_(self):
819 return "<b>dummy</b>"
829 return "<b>dummy</b>"
820
830
821 def _repr_javascript_(self):
831 def _repr_javascript_(self):
822 return "console.log('hi');", {'key': 'value'}
832 return "console.log('hi');", {'key': 'value'}
823
833
824
834
825 def test_user_variables():
835 def test_user_variables():
826 # enable all formatters
836 # enable all formatters
827 ip.display_formatter.active_types = ip.display_formatter.format_types
837 ip.display_formatter.active_types = ip.display_formatter.format_types
828
838
829 ip.user_ns['dummy'] = d = DummyRepr()
839 ip.user_ns['dummy'] = d = DummyRepr()
830 keys = {'dummy', 'doesnotexist'}
840 keys = {'dummy', 'doesnotexist'}
831 r = ip.user_expressions({ key:key for key in keys})
841 r = ip.user_expressions({ key:key for key in keys})
832
842
833 nt.assert_equal(keys, set(r.keys()))
843 nt.assert_equal(keys, set(r.keys()))
834 dummy = r['dummy']
844 dummy = r['dummy']
835 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
845 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
836 nt.assert_equal(dummy['status'], 'ok')
846 nt.assert_equal(dummy['status'], 'ok')
837 data = dummy['data']
847 data = dummy['data']
838 metadata = dummy['metadata']
848 metadata = dummy['metadata']
839 nt.assert_equal(data.get('text/html'), d._repr_html_())
849 nt.assert_equal(data.get('text/html'), d._repr_html_())
840 js, jsmd = d._repr_javascript_()
850 js, jsmd = d._repr_javascript_()
841 nt.assert_equal(data.get('application/javascript'), js)
851 nt.assert_equal(data.get('application/javascript'), js)
842 nt.assert_equal(metadata.get('application/javascript'), jsmd)
852 nt.assert_equal(metadata.get('application/javascript'), jsmd)
843
853
844 dne = r['doesnotexist']
854 dne = r['doesnotexist']
845 nt.assert_equal(dne['status'], 'error')
855 nt.assert_equal(dne['status'], 'error')
846 nt.assert_equal(dne['ename'], 'NameError')
856 nt.assert_equal(dne['ename'], 'NameError')
847
857
848 # back to text only
858 # back to text only
849 ip.display_formatter.active_types = ['text/plain']
859 ip.display_formatter.active_types = ['text/plain']
850
860
851 def test_user_expression():
861 def test_user_expression():
852 # enable all formatters
862 # enable all formatters
853 ip.display_formatter.active_types = ip.display_formatter.format_types
863 ip.display_formatter.active_types = ip.display_formatter.format_types
854 query = {
864 query = {
855 'a' : '1 + 2',
865 'a' : '1 + 2',
856 'b' : '1/0',
866 'b' : '1/0',
857 }
867 }
858 r = ip.user_expressions(query)
868 r = ip.user_expressions(query)
859 import pprint
869 import pprint
860 pprint.pprint(r)
870 pprint.pprint(r)
861 nt.assert_equal(set(r.keys()), set(query.keys()))
871 nt.assert_equal(set(r.keys()), set(query.keys()))
862 a = r['a']
872 a = r['a']
863 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
873 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
864 nt.assert_equal(a['status'], 'ok')
874 nt.assert_equal(a['status'], 'ok')
865 data = a['data']
875 data = a['data']
866 metadata = a['metadata']
876 metadata = a['metadata']
867 nt.assert_equal(data.get('text/plain'), '3')
877 nt.assert_equal(data.get('text/plain'), '3')
868
878
869 b = r['b']
879 b = r['b']
870 nt.assert_equal(b['status'], 'error')
880 nt.assert_equal(b['status'], 'error')
871 nt.assert_equal(b['ename'], 'ZeroDivisionError')
881 nt.assert_equal(b['ename'], 'ZeroDivisionError')
872
882
873 # back to text only
883 # back to text only
874 ip.display_formatter.active_types = ['text/plain']
884 ip.display_formatter.active_types = ['text/plain']
875
885
876
886
877
887
878
888
879
889
880 class TestSyntaxErrorTransformer(unittest.TestCase):
890 class TestSyntaxErrorTransformer(unittest.TestCase):
881 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
891 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
882
892
883 @staticmethod
893 @staticmethod
884 def transformer(lines):
894 def transformer(lines):
885 for line in lines:
895 for line in lines:
886 pos = line.find('syntaxerror')
896 pos = line.find('syntaxerror')
887 if pos >= 0:
897 if pos >= 0:
888 e = SyntaxError('input contains "syntaxerror"')
898 e = SyntaxError('input contains "syntaxerror"')
889 e.text = line
899 e.text = line
890 e.offset = pos + 1
900 e.offset = pos + 1
891 raise e
901 raise e
892 return lines
902 return lines
893
903
894 def setUp(self):
904 def setUp(self):
895 ip.input_transformers_post.append(self.transformer)
905 ip.input_transformers_post.append(self.transformer)
896
906
897 def tearDown(self):
907 def tearDown(self):
898 ip.input_transformers_post.remove(self.transformer)
908 ip.input_transformers_post.remove(self.transformer)
899
909
900 def test_syntaxerror_input_transformer(self):
910 def test_syntaxerror_input_transformer(self):
901 with tt.AssertPrints('1234'):
911 with tt.AssertPrints('1234'):
902 ip.run_cell('1234')
912 ip.run_cell('1234')
903 with tt.AssertPrints('SyntaxError: invalid syntax'):
913 with tt.AssertPrints('SyntaxError: invalid syntax'):
904 ip.run_cell('1 2 3') # plain python syntax error
914 ip.run_cell('1 2 3') # plain python syntax error
905 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
915 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
906 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
916 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
907 with tt.AssertPrints('3456'):
917 with tt.AssertPrints('3456'):
908 ip.run_cell('3456')
918 ip.run_cell('3456')
909
919
910
920
911
921
912 def test_warning_suppression():
922 def test_warning_suppression():
913 ip.run_cell("import warnings")
923 ip.run_cell("import warnings")
914 try:
924 try:
915 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
925 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
916 ip.run_cell("warnings.warn('asdf')")
926 ip.run_cell("warnings.warn('asdf')")
917 # Here's the real test -- if we run that again, we should get the
927 # Here's the real test -- if we run that again, we should get the
918 # warning again. Traditionally, each warning was only issued once per
928 # warning again. Traditionally, each warning was only issued once per
919 # IPython session (approximately), even if the user typed in new and
929 # IPython session (approximately), even if the user typed in new and
920 # different code that should have also triggered the warning, leading
930 # different code that should have also triggered the warning, leading
921 # to much confusion.
931 # to much confusion.
922 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
932 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
923 ip.run_cell("warnings.warn('asdf')")
933 ip.run_cell("warnings.warn('asdf')")
924 finally:
934 finally:
925 ip.run_cell("del warnings")
935 ip.run_cell("del warnings")
926
936
927
937
928 def test_deprecation_warning():
938 def test_deprecation_warning():
929 ip.run_cell("""
939 ip.run_cell("""
930 import warnings
940 import warnings
931 def wrn():
941 def wrn():
932 warnings.warn(
942 warnings.warn(
933 "I AM A WARNING",
943 "I AM A WARNING",
934 DeprecationWarning
944 DeprecationWarning
935 )
945 )
936 """)
946 """)
937 try:
947 try:
938 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
948 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
939 ip.run_cell("wrn()")
949 ip.run_cell("wrn()")
940 finally:
950 finally:
941 ip.run_cell("del warnings")
951 ip.run_cell("del warnings")
942 ip.run_cell("del wrn")
952 ip.run_cell("del wrn")
943
953
944
954
945 class TestImportNoDeprecate(tt.TempFileMixin):
955 class TestImportNoDeprecate(tt.TempFileMixin):
946
956
947 def setup(self):
957 def setUp(self):
948 """Make a valid python temp file."""
958 """Make a valid python temp file."""
949 self.mktmp("""
959 self.mktmp("""
950 import warnings
960 import warnings
951 def wrn():
961 def wrn():
952 warnings.warn(
962 warnings.warn(
953 "I AM A WARNING",
963 "I AM A WARNING",
954 DeprecationWarning
964 DeprecationWarning
955 )
965 )
956 """)
966 """)
967 super().setUp()
957
968
958 def test_no_dep(self):
969 def test_no_dep(self):
959 """
970 """
960 No deprecation warning should be raised from imported functions
971 No deprecation warning should be raised from imported functions
961 """
972 """
962 ip.run_cell("from {} import wrn".format(self.fname))
973 ip.run_cell("from {} import wrn".format(self.fname))
963
974
964 with tt.AssertNotPrints("I AM A WARNING"):
975 with tt.AssertNotPrints("I AM A WARNING"):
965 ip.run_cell("wrn()")
976 ip.run_cell("wrn()")
966 ip.run_cell("del wrn")
977 ip.run_cell("del wrn")
967
978
968
979
969 def test_custom_exc_count():
980 def test_custom_exc_count():
970 hook = mock.Mock(return_value=None)
981 hook = mock.Mock(return_value=None)
971 ip.set_custom_exc((SyntaxError,), hook)
982 ip.set_custom_exc((SyntaxError,), hook)
972 before = ip.execution_count
983 before = ip.execution_count
973 ip.run_cell("def foo()", store_history=True)
984 ip.run_cell("def foo()", store_history=True)
974 # restore default excepthook
985 # restore default excepthook
975 ip.set_custom_exc((), None)
986 ip.set_custom_exc((), None)
976 nt.assert_equal(hook.call_count, 1)
987 nt.assert_equal(hook.call_count, 1)
977 nt.assert_equal(ip.execution_count, before + 1)
988 nt.assert_equal(ip.execution_count, before + 1)
978
989
979
990
980 def test_run_cell_async():
991 def test_run_cell_async():
981 loop = asyncio.get_event_loop()
992 loop = asyncio.get_event_loop()
982 ip.run_cell("import asyncio")
993 ip.run_cell("import asyncio")
983 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
994 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
984 assert asyncio.iscoroutine(coro)
995 assert asyncio.iscoroutine(coro)
985 result = loop.run_until_complete(coro)
996 result = loop.run_until_complete(coro)
986 assert isinstance(result, interactiveshell.ExecutionResult)
997 assert isinstance(result, interactiveshell.ExecutionResult)
987 assert result.result == 5
998 assert result.result == 5
988
999
989
1000
990 def test_should_run_async():
1001 def test_should_run_async():
991 assert not ip.should_run_async("a = 5")
1002 assert not ip.should_run_async("a = 5")
992 assert ip.should_run_async("await x")
1003 assert ip.should_run_async("await x")
993 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1004 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
@@ -1,559 +1,559 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for code execution (%run and related), which is particularly tricky.
2 """Tests for code execution (%run and related), which is particularly tricky.
3
3
4 Because of how %run manages namespaces, and the fact that we are trying here to
4 Because of how %run manages namespaces, and the fact that we are trying here to
5 verify subtle object deletion and reference counting issues, the %run tests
5 verify subtle object deletion and reference counting issues, the %run tests
6 will be kept in this separate file. This makes it easier to aggregate in one
6 will be kept in this separate file. This makes it easier to aggregate in one
7 place the tricks needed to handle it; most other magics are much easier to test
7 place the tricks needed to handle it; most other magics are much easier to test
8 and we do so in a common test_magic file.
8 and we do so in a common test_magic file.
9
9
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 as otherwise it may influence later tests.
11 as otherwise it may influence later tests.
12 """
12 """
13
13
14 # Copyright (c) IPython Development Team.
14 # Copyright (c) IPython Development Team.
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16
16
17
17
18
18
19 import functools
19 import functools
20 import os
20 import os
21 from os.path import join as pjoin
21 from os.path import join as pjoin
22 import random
22 import random
23 import string
23 import string
24 import sys
24 import sys
25 import textwrap
25 import textwrap
26 import unittest
26 import unittest
27 from unittest.mock import patch
27 from unittest.mock import patch
28
28
29 import nose.tools as nt
29 import nose.tools as nt
30 from nose import SkipTest
30 from nose import SkipTest
31
31
32 from IPython.testing import decorators as dec
32 from IPython.testing import decorators as dec
33 from IPython.testing import tools as tt
33 from IPython.testing import tools as tt
34 from IPython.utils.io import capture_output
34 from IPython.utils.io import capture_output
35 from IPython.utils.tempdir import TemporaryDirectory
35 from IPython.utils.tempdir import TemporaryDirectory
36 from IPython.core import debugger
36 from IPython.core import debugger
37
37
38 def doctest_refbug():
38 def doctest_refbug():
39 """Very nasty problem with references held by multiple runs of a script.
39 """Very nasty problem with references held by multiple runs of a script.
40 See: https://github.com/ipython/ipython/issues/141
40 See: https://github.com/ipython/ipython/issues/141
41
41
42 In [1]: _ip.clear_main_mod_cache()
42 In [1]: _ip.clear_main_mod_cache()
43 # random
43 # random
44
44
45 In [2]: %run refbug
45 In [2]: %run refbug
46
46
47 In [3]: call_f()
47 In [3]: call_f()
48 lowercased: hello
48 lowercased: hello
49
49
50 In [4]: %run refbug
50 In [4]: %run refbug
51
51
52 In [5]: call_f()
52 In [5]: call_f()
53 lowercased: hello
53 lowercased: hello
54 lowercased: hello
54 lowercased: hello
55 """
55 """
56
56
57
57
58 def doctest_run_builtins():
58 def doctest_run_builtins():
59 r"""Check that %run doesn't damage __builtins__.
59 r"""Check that %run doesn't damage __builtins__.
60
60
61 In [1]: import tempfile
61 In [1]: import tempfile
62
62
63 In [2]: bid1 = id(__builtins__)
63 In [2]: bid1 = id(__builtins__)
64
64
65 In [3]: fname = tempfile.mkstemp('.py')[1]
65 In [3]: fname = tempfile.mkstemp('.py')[1]
66
66
67 In [3]: f = open(fname,'w')
67 In [3]: f = open(fname,'w')
68
68
69 In [4]: dummy= f.write('pass\n')
69 In [4]: dummy= f.write('pass\n')
70
70
71 In [5]: f.flush()
71 In [5]: f.flush()
72
72
73 In [6]: t1 = type(__builtins__)
73 In [6]: t1 = type(__builtins__)
74
74
75 In [7]: %run $fname
75 In [7]: %run $fname
76
76
77 In [7]: f.close()
77 In [7]: f.close()
78
78
79 In [8]: bid2 = id(__builtins__)
79 In [8]: bid2 = id(__builtins__)
80
80
81 In [9]: t2 = type(__builtins__)
81 In [9]: t2 = type(__builtins__)
82
82
83 In [10]: t1 == t2
83 In [10]: t1 == t2
84 Out[10]: True
84 Out[10]: True
85
85
86 In [10]: bid1 == bid2
86 In [10]: bid1 == bid2
87 Out[10]: True
87 Out[10]: True
88
88
89 In [12]: try:
89 In [12]: try:
90 ....: os.unlink(fname)
90 ....: os.unlink(fname)
91 ....: except:
91 ....: except:
92 ....: pass
92 ....: pass
93 ....:
93 ....:
94 """
94 """
95
95
96
96
97 def doctest_run_option_parser():
97 def doctest_run_option_parser():
98 r"""Test option parser in %run.
98 r"""Test option parser in %run.
99
99
100 In [1]: %run print_argv.py
100 In [1]: %run print_argv.py
101 []
101 []
102
102
103 In [2]: %run print_argv.py print*.py
103 In [2]: %run print_argv.py print*.py
104 ['print_argv.py']
104 ['print_argv.py']
105
105
106 In [3]: %run -G print_argv.py print*.py
106 In [3]: %run -G print_argv.py print*.py
107 ['print*.py']
107 ['print*.py']
108
108
109 """
109 """
110
110
111
111
112 @dec.skip_win32
112 @dec.skip_win32
113 def doctest_run_option_parser_for_posix():
113 def doctest_run_option_parser_for_posix():
114 r"""Test option parser in %run (Linux/OSX specific).
114 r"""Test option parser in %run (Linux/OSX specific).
115
115
116 You need double quote to escape glob in POSIX systems:
116 You need double quote to escape glob in POSIX systems:
117
117
118 In [1]: %run print_argv.py print\\*.py
118 In [1]: %run print_argv.py print\\*.py
119 ['print*.py']
119 ['print*.py']
120
120
121 You can't use quote to escape glob in POSIX systems:
121 You can't use quote to escape glob in POSIX systems:
122
122
123 In [2]: %run print_argv.py 'print*.py'
123 In [2]: %run print_argv.py 'print*.py'
124 ['print_argv.py']
124 ['print_argv.py']
125
125
126 """
126 """
127
127
128
128
129 @dec.skip_if_not_win32
129 @dec.skip_if_not_win32
130 def doctest_run_option_parser_for_windows():
130 def doctest_run_option_parser_for_windows():
131 r"""Test option parser in %run (Windows specific).
131 r"""Test option parser in %run (Windows specific).
132
132
133 In Windows, you can't escape ``*` `by backslash:
133 In Windows, you can't escape ``*` `by backslash:
134
134
135 In [1]: %run print_argv.py print\\*.py
135 In [1]: %run print_argv.py print\\*.py
136 ['print\\*.py']
136 ['print\\*.py']
137
137
138 You can use quote to escape glob:
138 You can use quote to escape glob:
139
139
140 In [2]: %run print_argv.py 'print*.py'
140 In [2]: %run print_argv.py 'print*.py'
141 ['print*.py']
141 ['print*.py']
142
142
143 """
143 """
144
144
145
145
146 def doctest_reset_del():
146 def doctest_reset_del():
147 """Test that resetting doesn't cause errors in __del__ methods.
147 """Test that resetting doesn't cause errors in __del__ methods.
148
148
149 In [2]: class A(object):
149 In [2]: class A(object):
150 ...: def __del__(self):
150 ...: def __del__(self):
151 ...: print(str("Hi"))
151 ...: print(str("Hi"))
152 ...:
152 ...:
153
153
154 In [3]: a = A()
154 In [3]: a = A()
155
155
156 In [4]: get_ipython().reset()
156 In [4]: get_ipython().reset()
157 Hi
157 Hi
158
158
159 In [5]: 1+1
159 In [5]: 1+1
160 Out[5]: 2
160 Out[5]: 2
161 """
161 """
162
162
163 # For some tests, it will be handy to organize them in a class with a common
163 # For some tests, it will be handy to organize them in a class with a common
164 # setup that makes a temp file
164 # setup that makes a temp file
165
165
166 class TestMagicRunPass(tt.TempFileMixin):
166 class TestMagicRunPass(tt.TempFileMixin):
167
167
168 def setup(self):
168 def setUp(self):
169 content = "a = [1,2,3]\nb = 1"
169 content = "a = [1,2,3]\nb = 1"
170 self.mktmp(content)
170 self.mktmp(content)
171
171
172 def run_tmpfile(self):
172 def run_tmpfile(self):
173 _ip = get_ipython()
173 _ip = get_ipython()
174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
175 # See below and ticket https://bugs.launchpad.net/bugs/366353
175 # See below and ticket https://bugs.launchpad.net/bugs/366353
176 _ip.magic('run %s' % self.fname)
176 _ip.magic('run %s' % self.fname)
177
177
178 def run_tmpfile_p(self):
178 def run_tmpfile_p(self):
179 _ip = get_ipython()
179 _ip = get_ipython()
180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 # See below and ticket https://bugs.launchpad.net/bugs/366353
181 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 _ip.magic('run -p %s' % self.fname)
182 _ip.magic('run -p %s' % self.fname)
183
183
184 def test_builtins_id(self):
184 def test_builtins_id(self):
185 """Check that %run doesn't damage __builtins__ """
185 """Check that %run doesn't damage __builtins__ """
186 _ip = get_ipython()
186 _ip = get_ipython()
187 # Test that the id of __builtins__ is not modified by %run
187 # Test that the id of __builtins__ is not modified by %run
188 bid1 = id(_ip.user_ns['__builtins__'])
188 bid1 = id(_ip.user_ns['__builtins__'])
189 self.run_tmpfile()
189 self.run_tmpfile()
190 bid2 = id(_ip.user_ns['__builtins__'])
190 bid2 = id(_ip.user_ns['__builtins__'])
191 nt.assert_equal(bid1, bid2)
191 nt.assert_equal(bid1, bid2)
192
192
193 def test_builtins_type(self):
193 def test_builtins_type(self):
194 """Check that the type of __builtins__ doesn't change with %run.
194 """Check that the type of __builtins__ doesn't change with %run.
195
195
196 However, the above could pass if __builtins__ was already modified to
196 However, the above could pass if __builtins__ was already modified to
197 be a dict (it should be a module) by a previous use of %run. So we
197 be a dict (it should be a module) by a previous use of %run. So we
198 also check explicitly that it really is a module:
198 also check explicitly that it really is a module:
199 """
199 """
200 _ip = get_ipython()
200 _ip = get_ipython()
201 self.run_tmpfile()
201 self.run_tmpfile()
202 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
202 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
203
203
204 def test_run_profile( self ):
204 def test_run_profile( self ):
205 """Test that the option -p, which invokes the profiler, do not
205 """Test that the option -p, which invokes the profiler, do not
206 crash by invoking execfile"""
206 crash by invoking execfile"""
207 self.run_tmpfile_p()
207 self.run_tmpfile_p()
208
208
209 def test_run_debug_twice(self):
209 def test_run_debug_twice(self):
210 # https://github.com/ipython/ipython/issues/10028
210 # https://github.com/ipython/ipython/issues/10028
211 _ip = get_ipython()
211 _ip = get_ipython()
212 with tt.fake_input(['c']):
212 with tt.fake_input(['c']):
213 _ip.magic('run -d %s' % self.fname)
213 _ip.magic('run -d %s' % self.fname)
214 with tt.fake_input(['c']):
214 with tt.fake_input(['c']):
215 _ip.magic('run -d %s' % self.fname)
215 _ip.magic('run -d %s' % self.fname)
216
216
217 def test_run_debug_twice_with_breakpoint(self):
217 def test_run_debug_twice_with_breakpoint(self):
218 """Make a valid python temp file."""
218 """Make a valid python temp file."""
219 _ip = get_ipython()
219 _ip = get_ipython()
220 with tt.fake_input(['b 2', 'c', 'c']):
220 with tt.fake_input(['b 2', 'c', 'c']):
221 _ip.magic('run -d %s' % self.fname)
221 _ip.magic('run -d %s' % self.fname)
222
222
223 with tt.fake_input(['c']):
223 with tt.fake_input(['c']):
224 with tt.AssertNotPrints('KeyError'):
224 with tt.AssertNotPrints('KeyError'):
225 _ip.magic('run -d %s' % self.fname)
225 _ip.magic('run -d %s' % self.fname)
226
226
227
227
228 class TestMagicRunSimple(tt.TempFileMixin):
228 class TestMagicRunSimple(tt.TempFileMixin):
229
229
230 def test_simpledef(self):
230 def test_simpledef(self):
231 """Test that simple class definitions work."""
231 """Test that simple class definitions work."""
232 src = ("class foo: pass\n"
232 src = ("class foo: pass\n"
233 "def f(): return foo()")
233 "def f(): return foo()")
234 self.mktmp(src)
234 self.mktmp(src)
235 _ip.magic('run %s' % self.fname)
235 _ip.magic('run %s' % self.fname)
236 _ip.run_cell('t = isinstance(f(), foo)')
236 _ip.run_cell('t = isinstance(f(), foo)')
237 nt.assert_true(_ip.user_ns['t'])
237 nt.assert_true(_ip.user_ns['t'])
238
238
239 def test_obj_del(self):
239 def test_obj_del(self):
240 """Test that object's __del__ methods are called on exit."""
240 """Test that object's __del__ methods are called on exit."""
241 if sys.platform == 'win32':
241 if sys.platform == 'win32':
242 try:
242 try:
243 import win32api
243 import win32api
244 except ImportError:
244 except ImportError:
245 raise SkipTest("Test requires pywin32")
245 raise SkipTest("Test requires pywin32")
246 src = ("class A(object):\n"
246 src = ("class A(object):\n"
247 " def __del__(self):\n"
247 " def __del__(self):\n"
248 " print('object A deleted')\n"
248 " print('object A deleted')\n"
249 "a = A()\n")
249 "a = A()\n")
250 self.mktmp(src)
250 self.mktmp(src)
251 if dec.module_not_available('sqlite3'):
251 if dec.module_not_available('sqlite3'):
252 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
252 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
253 else:
253 else:
254 err = None
254 err = None
255 tt.ipexec_validate(self.fname, 'object A deleted', err)
255 tt.ipexec_validate(self.fname, 'object A deleted', err)
256
256
257 def test_aggressive_namespace_cleanup(self):
257 def test_aggressive_namespace_cleanup(self):
258 """Test that namespace cleanup is not too aggressive GH-238
258 """Test that namespace cleanup is not too aggressive GH-238
259
259
260 Returning from another run magic deletes the namespace"""
260 Returning from another run magic deletes the namespace"""
261 # see ticket https://github.com/ipython/ipython/issues/238
261 # see ticket https://github.com/ipython/ipython/issues/238
262
262
263 with tt.TempFileMixin() as empty:
263 with tt.TempFileMixin() as empty:
264 empty.mktmp('')
264 empty.mktmp('')
265 # On Windows, the filename will have \users in it, so we need to use the
265 # On Windows, the filename will have \users in it, so we need to use the
266 # repr so that the \u becomes \\u.
266 # repr so that the \u becomes \\u.
267 src = ("ip = get_ipython()\n"
267 src = ("ip = get_ipython()\n"
268 "for i in range(5):\n"
268 "for i in range(5):\n"
269 " try:\n"
269 " try:\n"
270 " ip.magic(%r)\n"
270 " ip.magic(%r)\n"
271 " except NameError as e:\n"
271 " except NameError as e:\n"
272 " print(i)\n"
272 " print(i)\n"
273 " break\n" % ('run ' + empty.fname))
273 " break\n" % ('run ' + empty.fname))
274 self.mktmp(src)
274 self.mktmp(src)
275 _ip.magic('run %s' % self.fname)
275 _ip.magic('run %s' % self.fname)
276 _ip.run_cell('ip == get_ipython()')
276 _ip.run_cell('ip == get_ipython()')
277 nt.assert_equal(_ip.user_ns['i'], 4)
277 nt.assert_equal(_ip.user_ns['i'], 4)
278
278
279 def test_run_second(self):
279 def test_run_second(self):
280 """Test that running a second file doesn't clobber the first, gh-3547
280 """Test that running a second file doesn't clobber the first, gh-3547
281 """
281 """
282 self.mktmp("avar = 1\n"
282 self.mktmp("avar = 1\n"
283 "def afunc():\n"
283 "def afunc():\n"
284 " return avar\n")
284 " return avar\n")
285
285
286 with tt.TempFileMixin() as empty:
286 with tt.TempFileMixin() as empty:
287 empty.mktmp("")
287 empty.mktmp("")
288
288
289 _ip.magic('run %s' % self.fname)
289 _ip.magic('run %s' % self.fname)
290 _ip.magic('run %s' % empty.fname)
290 _ip.magic('run %s' % empty.fname)
291 nt.assert_equal(_ip.user_ns['afunc'](), 1)
291 nt.assert_equal(_ip.user_ns['afunc'](), 1)
292
292
293 @dec.skip_win32
293 @dec.skip_win32
294 def test_tclass(self):
294 def test_tclass(self):
295 mydir = os.path.dirname(__file__)
295 mydir = os.path.dirname(__file__)
296 tc = os.path.join(mydir, 'tclass')
296 tc = os.path.join(mydir, 'tclass')
297 src = ("%%run '%s' C-first\n"
297 src = ("%%run '%s' C-first\n"
298 "%%run '%s' C-second\n"
298 "%%run '%s' C-second\n"
299 "%%run '%s' C-third\n") % (tc, tc, tc)
299 "%%run '%s' C-third\n") % (tc, tc, tc)
300 self.mktmp(src, '.ipy')
300 self.mktmp(src, '.ipy')
301 out = """\
301 out = """\
302 ARGV 1-: ['C-first']
302 ARGV 1-: ['C-first']
303 ARGV 1-: ['C-second']
303 ARGV 1-: ['C-second']
304 tclass.py: deleting object: C-first
304 tclass.py: deleting object: C-first
305 ARGV 1-: ['C-third']
305 ARGV 1-: ['C-third']
306 tclass.py: deleting object: C-second
306 tclass.py: deleting object: C-second
307 tclass.py: deleting object: C-third
307 tclass.py: deleting object: C-third
308 """
308 """
309 if dec.module_not_available('sqlite3'):
309 if dec.module_not_available('sqlite3'):
310 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
310 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
311 else:
311 else:
312 err = None
312 err = None
313 tt.ipexec_validate(self.fname, out, err)
313 tt.ipexec_validate(self.fname, out, err)
314
314
315 def test_run_i_after_reset(self):
315 def test_run_i_after_reset(self):
316 """Check that %run -i still works after %reset (gh-693)"""
316 """Check that %run -i still works after %reset (gh-693)"""
317 src = "yy = zz\n"
317 src = "yy = zz\n"
318 self.mktmp(src)
318 self.mktmp(src)
319 _ip.run_cell("zz = 23")
319 _ip.run_cell("zz = 23")
320 try:
320 try:
321 _ip.magic('run -i %s' % self.fname)
321 _ip.magic('run -i %s' % self.fname)
322 nt.assert_equal(_ip.user_ns['yy'], 23)
322 nt.assert_equal(_ip.user_ns['yy'], 23)
323 finally:
323 finally:
324 _ip.magic('reset -f')
324 _ip.magic('reset -f')
325
325
326 _ip.run_cell("zz = 23")
326 _ip.run_cell("zz = 23")
327 try:
327 try:
328 _ip.magic('run -i %s' % self.fname)
328 _ip.magic('run -i %s' % self.fname)
329 nt.assert_equal(_ip.user_ns['yy'], 23)
329 nt.assert_equal(_ip.user_ns['yy'], 23)
330 finally:
330 finally:
331 _ip.magic('reset -f')
331 _ip.magic('reset -f')
332
332
333 def test_unicode(self):
333 def test_unicode(self):
334 """Check that files in odd encodings are accepted."""
334 """Check that files in odd encodings are accepted."""
335 mydir = os.path.dirname(__file__)
335 mydir = os.path.dirname(__file__)
336 na = os.path.join(mydir, 'nonascii.py')
336 na = os.path.join(mydir, 'nonascii.py')
337 _ip.magic('run "%s"' % na)
337 _ip.magic('run "%s"' % na)
338 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
338 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
339
339
340 def test_run_py_file_attribute(self):
340 def test_run_py_file_attribute(self):
341 """Test handling of `__file__` attribute in `%run <file>.py`."""
341 """Test handling of `__file__` attribute in `%run <file>.py`."""
342 src = "t = __file__\n"
342 src = "t = __file__\n"
343 self.mktmp(src)
343 self.mktmp(src)
344 _missing = object()
344 _missing = object()
345 file1 = _ip.user_ns.get('__file__', _missing)
345 file1 = _ip.user_ns.get('__file__', _missing)
346 _ip.magic('run %s' % self.fname)
346 _ip.magic('run %s' % self.fname)
347 file2 = _ip.user_ns.get('__file__', _missing)
347 file2 = _ip.user_ns.get('__file__', _missing)
348
348
349 # Check that __file__ was equal to the filename in the script's
349 # Check that __file__ was equal to the filename in the script's
350 # namespace.
350 # namespace.
351 nt.assert_equal(_ip.user_ns['t'], self.fname)
351 nt.assert_equal(_ip.user_ns['t'], self.fname)
352
352
353 # Check that __file__ was not leaked back into user_ns.
353 # Check that __file__ was not leaked back into user_ns.
354 nt.assert_equal(file1, file2)
354 nt.assert_equal(file1, file2)
355
355
356 def test_run_ipy_file_attribute(self):
356 def test_run_ipy_file_attribute(self):
357 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
357 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
358 src = "t = __file__\n"
358 src = "t = __file__\n"
359 self.mktmp(src, ext='.ipy')
359 self.mktmp(src, ext='.ipy')
360 _missing = object()
360 _missing = object()
361 file1 = _ip.user_ns.get('__file__', _missing)
361 file1 = _ip.user_ns.get('__file__', _missing)
362 _ip.magic('run %s' % self.fname)
362 _ip.magic('run %s' % self.fname)
363 file2 = _ip.user_ns.get('__file__', _missing)
363 file2 = _ip.user_ns.get('__file__', _missing)
364
364
365 # Check that __file__ was equal to the filename in the script's
365 # Check that __file__ was equal to the filename in the script's
366 # namespace.
366 # namespace.
367 nt.assert_equal(_ip.user_ns['t'], self.fname)
367 nt.assert_equal(_ip.user_ns['t'], self.fname)
368
368
369 # Check that __file__ was not leaked back into user_ns.
369 # Check that __file__ was not leaked back into user_ns.
370 nt.assert_equal(file1, file2)
370 nt.assert_equal(file1, file2)
371
371
372 def test_run_formatting(self):
372 def test_run_formatting(self):
373 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
373 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
374 src = "pass"
374 src = "pass"
375 self.mktmp(src)
375 self.mktmp(src)
376 _ip.magic('run -t -N 1 %s' % self.fname)
376 _ip.magic('run -t -N 1 %s' % self.fname)
377 _ip.magic('run -t -N 10 %s' % self.fname)
377 _ip.magic('run -t -N 10 %s' % self.fname)
378
378
379 def test_ignore_sys_exit(self):
379 def test_ignore_sys_exit(self):
380 """Test the -e option to ignore sys.exit()"""
380 """Test the -e option to ignore sys.exit()"""
381 src = "import sys; sys.exit(1)"
381 src = "import sys; sys.exit(1)"
382 self.mktmp(src)
382 self.mktmp(src)
383 with tt.AssertPrints('SystemExit'):
383 with tt.AssertPrints('SystemExit'):
384 _ip.magic('run %s' % self.fname)
384 _ip.magic('run %s' % self.fname)
385
385
386 with tt.AssertNotPrints('SystemExit'):
386 with tt.AssertNotPrints('SystemExit'):
387 _ip.magic('run -e %s' % self.fname)
387 _ip.magic('run -e %s' % self.fname)
388
388
389 def test_run_nb(self):
389 def test_run_nb(self):
390 """Test %run notebook.ipynb"""
390 """Test %run notebook.ipynb"""
391 from nbformat import v4, writes
391 from nbformat import v4, writes
392 nb = v4.new_notebook(
392 nb = v4.new_notebook(
393 cells=[
393 cells=[
394 v4.new_markdown_cell("The Ultimate Question of Everything"),
394 v4.new_markdown_cell("The Ultimate Question of Everything"),
395 v4.new_code_cell("answer=42")
395 v4.new_code_cell("answer=42")
396 ]
396 ]
397 )
397 )
398 src = writes(nb, version=4)
398 src = writes(nb, version=4)
399 self.mktmp(src, ext='.ipynb')
399 self.mktmp(src, ext='.ipynb')
400
400
401 _ip.magic("run %s" % self.fname)
401 _ip.magic("run %s" % self.fname)
402
402
403 nt.assert_equal(_ip.user_ns['answer'], 42)
403 nt.assert_equal(_ip.user_ns['answer'], 42)
404
404
405 def test_file_options(self):
405 def test_file_options(self):
406 src = ('import sys\n'
406 src = ('import sys\n'
407 'a = " ".join(sys.argv[1:])\n')
407 'a = " ".join(sys.argv[1:])\n')
408 self.mktmp(src)
408 self.mktmp(src)
409 test_opts = '-x 3 --verbose'
409 test_opts = '-x 3 --verbose'
410 _ip.run_line_magic("run", '{0} {1}'.format(self.fname, test_opts))
410 _ip.run_line_magic("run", '{0} {1}'.format(self.fname, test_opts))
411 nt.assert_equal(_ip.user_ns['a'], test_opts)
411 nt.assert_equal(_ip.user_ns['a'], test_opts)
412
412
413
413
414 class TestMagicRunWithPackage(unittest.TestCase):
414 class TestMagicRunWithPackage(unittest.TestCase):
415
415
416 def writefile(self, name, content):
416 def writefile(self, name, content):
417 path = os.path.join(self.tempdir.name, name)
417 path = os.path.join(self.tempdir.name, name)
418 d = os.path.dirname(path)
418 d = os.path.dirname(path)
419 if not os.path.isdir(d):
419 if not os.path.isdir(d):
420 os.makedirs(d)
420 os.makedirs(d)
421 with open(path, 'w') as f:
421 with open(path, 'w') as f:
422 f.write(textwrap.dedent(content))
422 f.write(textwrap.dedent(content))
423
423
424 def setUp(self):
424 def setUp(self):
425 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
425 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
426 """Temporary (probably) valid python package name."""
426 """Temporary (probably) valid python package name."""
427
427
428 self.value = int(random.random() * 10000)
428 self.value = int(random.random() * 10000)
429
429
430 self.tempdir = TemporaryDirectory()
430 self.tempdir = TemporaryDirectory()
431 self.__orig_cwd = os.getcwd()
431 self.__orig_cwd = os.getcwd()
432 sys.path.insert(0, self.tempdir.name)
432 sys.path.insert(0, self.tempdir.name)
433
433
434 self.writefile(os.path.join(package, '__init__.py'), '')
434 self.writefile(os.path.join(package, '__init__.py'), '')
435 self.writefile(os.path.join(package, 'sub.py'), """
435 self.writefile(os.path.join(package, 'sub.py'), """
436 x = {0!r}
436 x = {0!r}
437 """.format(self.value))
437 """.format(self.value))
438 self.writefile(os.path.join(package, 'relative.py'), """
438 self.writefile(os.path.join(package, 'relative.py'), """
439 from .sub import x
439 from .sub import x
440 """)
440 """)
441 self.writefile(os.path.join(package, 'absolute.py'), """
441 self.writefile(os.path.join(package, 'absolute.py'), """
442 from {0}.sub import x
442 from {0}.sub import x
443 """.format(package))
443 """.format(package))
444 self.writefile(os.path.join(package, 'args.py'), """
444 self.writefile(os.path.join(package, 'args.py'), """
445 import sys
445 import sys
446 a = " ".join(sys.argv[1:])
446 a = " ".join(sys.argv[1:])
447 """.format(package))
447 """.format(package))
448
448
449 def tearDown(self):
449 def tearDown(self):
450 os.chdir(self.__orig_cwd)
450 os.chdir(self.__orig_cwd)
451 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
451 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
452 self.tempdir.cleanup()
452 self.tempdir.cleanup()
453
453
454 def check_run_submodule(self, submodule, opts=''):
454 def check_run_submodule(self, submodule, opts=''):
455 _ip.user_ns.pop('x', None)
455 _ip.user_ns.pop('x', None)
456 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
456 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
457 self.assertEqual(_ip.user_ns['x'], self.value,
457 self.assertEqual(_ip.user_ns['x'], self.value,
458 'Variable `x` is not loaded from module `{0}`.'
458 'Variable `x` is not loaded from module `{0}`.'
459 .format(submodule))
459 .format(submodule))
460
460
461 def test_run_submodule_with_absolute_import(self):
461 def test_run_submodule_with_absolute_import(self):
462 self.check_run_submodule('absolute')
462 self.check_run_submodule('absolute')
463
463
464 def test_run_submodule_with_relative_import(self):
464 def test_run_submodule_with_relative_import(self):
465 """Run submodule that has a relative import statement (#2727)."""
465 """Run submodule that has a relative import statement (#2727)."""
466 self.check_run_submodule('relative')
466 self.check_run_submodule('relative')
467
467
468 def test_prun_submodule_with_absolute_import(self):
468 def test_prun_submodule_with_absolute_import(self):
469 self.check_run_submodule('absolute', '-p')
469 self.check_run_submodule('absolute', '-p')
470
470
471 def test_prun_submodule_with_relative_import(self):
471 def test_prun_submodule_with_relative_import(self):
472 self.check_run_submodule('relative', '-p')
472 self.check_run_submodule('relative', '-p')
473
473
474 def with_fake_debugger(func):
474 def with_fake_debugger(func):
475 @functools.wraps(func)
475 @functools.wraps(func)
476 def wrapper(*args, **kwds):
476 def wrapper(*args, **kwds):
477 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
477 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
478 return func(*args, **kwds)
478 return func(*args, **kwds)
479 return wrapper
479 return wrapper
480
480
481 @with_fake_debugger
481 @with_fake_debugger
482 def test_debug_run_submodule_with_absolute_import(self):
482 def test_debug_run_submodule_with_absolute_import(self):
483 self.check_run_submodule('absolute', '-d')
483 self.check_run_submodule('absolute', '-d')
484
484
485 @with_fake_debugger
485 @with_fake_debugger
486 def test_debug_run_submodule_with_relative_import(self):
486 def test_debug_run_submodule_with_relative_import(self):
487 self.check_run_submodule('relative', '-d')
487 self.check_run_submodule('relative', '-d')
488
488
489 def test_module_options(self):
489 def test_module_options(self):
490 _ip.user_ns.pop('a', None)
490 _ip.user_ns.pop('a', None)
491 test_opts = '-x abc -m test'
491 test_opts = '-x abc -m test'
492 _ip.run_line_magic('run', '-m {0}.args {1}'.format(self.package, test_opts))
492 _ip.run_line_magic('run', '-m {0}.args {1}'.format(self.package, test_opts))
493 nt.assert_equal(_ip.user_ns['a'], test_opts)
493 nt.assert_equal(_ip.user_ns['a'], test_opts)
494
494
495 def test_module_options_with_separator(self):
495 def test_module_options_with_separator(self):
496 _ip.user_ns.pop('a', None)
496 _ip.user_ns.pop('a', None)
497 test_opts = '-x abc -m test'
497 test_opts = '-x abc -m test'
498 _ip.run_line_magic('run', '-m {0}.args -- {1}'.format(self.package, test_opts))
498 _ip.run_line_magic('run', '-m {0}.args -- {1}'.format(self.package, test_opts))
499 nt.assert_equal(_ip.user_ns['a'], test_opts)
499 nt.assert_equal(_ip.user_ns['a'], test_opts)
500
500
501 def test_run__name__():
501 def test_run__name__():
502 with TemporaryDirectory() as td:
502 with TemporaryDirectory() as td:
503 path = pjoin(td, 'foo.py')
503 path = pjoin(td, 'foo.py')
504 with open(path, 'w') as f:
504 with open(path, 'w') as f:
505 f.write("q = __name__")
505 f.write("q = __name__")
506
506
507 _ip.user_ns.pop('q', None)
507 _ip.user_ns.pop('q', None)
508 _ip.magic('run {}'.format(path))
508 _ip.magic('run {}'.format(path))
509 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
509 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
510
510
511 _ip.magic('run -n {}'.format(path))
511 _ip.magic('run -n {}'.format(path))
512 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
512 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
513
513
514 try:
514 try:
515 _ip.magic('run -i -n {}'.format(path))
515 _ip.magic('run -i -n {}'.format(path))
516 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
516 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
517 finally:
517 finally:
518 _ip.magic('reset -f')
518 _ip.magic('reset -f')
519
519
520
520
521 def test_run_tb():
521 def test_run_tb():
522 """Test traceback offset in %run"""
522 """Test traceback offset in %run"""
523 with TemporaryDirectory() as td:
523 with TemporaryDirectory() as td:
524 path = pjoin(td, 'foo.py')
524 path = pjoin(td, 'foo.py')
525 with open(path, 'w') as f:
525 with open(path, 'w') as f:
526 f.write('\n'.join([
526 f.write('\n'.join([
527 "def foo():",
527 "def foo():",
528 " return bar()",
528 " return bar()",
529 "def bar():",
529 "def bar():",
530 " raise RuntimeError('hello!')",
530 " raise RuntimeError('hello!')",
531 "foo()",
531 "foo()",
532 ]))
532 ]))
533 with capture_output() as io:
533 with capture_output() as io:
534 _ip.magic('run {}'.format(path))
534 _ip.magic('run {}'.format(path))
535 out = io.stdout
535 out = io.stdout
536 nt.assert_not_in("execfile", out)
536 nt.assert_not_in("execfile", out)
537 nt.assert_in("RuntimeError", out)
537 nt.assert_in("RuntimeError", out)
538 nt.assert_equal(out.count("---->"), 3)
538 nt.assert_equal(out.count("---->"), 3)
539 del ip.user_ns['bar']
539 del ip.user_ns['bar']
540 del ip.user_ns['foo']
540 del ip.user_ns['foo']
541
541
542 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
542 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
543 def test_script_tb():
543 def test_script_tb():
544 """Test traceback offset in `ipython script.py`"""
544 """Test traceback offset in `ipython script.py`"""
545 with TemporaryDirectory() as td:
545 with TemporaryDirectory() as td:
546 path = pjoin(td, 'foo.py')
546 path = pjoin(td, 'foo.py')
547 with open(path, 'w') as f:
547 with open(path, 'w') as f:
548 f.write('\n'.join([
548 f.write('\n'.join([
549 "def foo():",
549 "def foo():",
550 " return bar()",
550 " return bar()",
551 "def bar():",
551 "def bar():",
552 " raise RuntimeError('hello!')",
552 " raise RuntimeError('hello!')",
553 "foo()",
553 "foo()",
554 ]))
554 ]))
555 out, err = tt.ipexec(path)
555 out, err = tt.ipexec(path)
556 nt.assert_not_in("execfile", out)
556 nt.assert_not_in("execfile", out)
557 nt.assert_in("RuntimeError", out)
557 nt.assert_in("RuntimeError", out)
558 nt.assert_equal(out.count("---->"), 3)
558 nt.assert_equal(out.count("---->"), 3)
559
559
@@ -1,449 +1,451 b''
1 """Tests for autoreload extension.
1 """Tests for autoreload extension.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 IPython Development Team.
4 # Copyright (c) 2012 IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import textwrap
18 import textwrap
19 import shutil
19 import shutil
20 import random
20 import random
21 import time
21 import time
22 from io import StringIO
22 from io import StringIO
23
23
24 import nose.tools as nt
24 import nose.tools as nt
25 import IPython.testing.tools as tt
25 import IPython.testing.tools as tt
26
26
27 from unittest import TestCase
28
27 from IPython.testing.decorators import skipif
29 from IPython.testing.decorators import skipif
28
30
29 from IPython.extensions.autoreload import AutoreloadMagics
31 from IPython.extensions.autoreload import AutoreloadMagics
30 from IPython.core.events import EventManager, pre_run_cell
32 from IPython.core.events import EventManager, pre_run_cell
31
33
32 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
33 # Test fixture
35 # Test fixture
34 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
35
37
36 noop = lambda *a, **kw: None
38 noop = lambda *a, **kw: None
37
39
38 class FakeShell:
40 class FakeShell:
39
41
40 def __init__(self):
42 def __init__(self):
41 self.ns = {}
43 self.ns = {}
42 self.user_ns = self.ns
44 self.user_ns = self.ns
43 self.user_ns_hidden = {}
45 self.user_ns_hidden = {}
44 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
46 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
45 self.auto_magics = AutoreloadMagics(shell=self)
47 self.auto_magics = AutoreloadMagics(shell=self)
46 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
48 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
47
49
48 register_magics = set_hook = noop
50 register_magics = set_hook = noop
49
51
50 def run_code(self, code):
52 def run_code(self, code):
51 self.events.trigger('pre_run_cell')
53 self.events.trigger('pre_run_cell')
52 exec(code, self.user_ns)
54 exec(code, self.user_ns)
53 self.auto_magics.post_execute_hook()
55 self.auto_magics.post_execute_hook()
54
56
55 def push(self, items):
57 def push(self, items):
56 self.ns.update(items)
58 self.ns.update(items)
57
59
58 def magic_autoreload(self, parameter):
60 def magic_autoreload(self, parameter):
59 self.auto_magics.autoreload(parameter)
61 self.auto_magics.autoreload(parameter)
60
62
61 def magic_aimport(self, parameter, stream=None):
63 def magic_aimport(self, parameter, stream=None):
62 self.auto_magics.aimport(parameter, stream=stream)
64 self.auto_magics.aimport(parameter, stream=stream)
63 self.auto_magics.post_execute_hook()
65 self.auto_magics.post_execute_hook()
64
66
65
67
66 class Fixture(object):
68 class Fixture(TestCase):
67 """Fixture for creating test module files"""
69 """Fixture for creating test module files"""
68
70
69 test_dir = None
71 test_dir = None
70 old_sys_path = None
72 old_sys_path = None
71 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
73 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
72
74
73 def setUp(self):
75 def setUp(self):
74 self.test_dir = tempfile.mkdtemp()
76 self.test_dir = tempfile.mkdtemp()
75 self.old_sys_path = list(sys.path)
77 self.old_sys_path = list(sys.path)
76 sys.path.insert(0, self.test_dir)
78 sys.path.insert(0, self.test_dir)
77 self.shell = FakeShell()
79 self.shell = FakeShell()
78
80
79 def tearDown(self):
81 def tearDown(self):
80 shutil.rmtree(self.test_dir)
82 shutil.rmtree(self.test_dir)
81 sys.path = self.old_sys_path
83 sys.path = self.old_sys_path
82
84
83 self.test_dir = None
85 self.test_dir = None
84 self.old_sys_path = None
86 self.old_sys_path = None
85 self.shell = None
87 self.shell = None
86
88
87 def get_module(self):
89 def get_module(self):
88 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
90 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
89 if module_name in sys.modules:
91 if module_name in sys.modules:
90 del sys.modules[module_name]
92 del sys.modules[module_name]
91 file_name = os.path.join(self.test_dir, module_name + ".py")
93 file_name = os.path.join(self.test_dir, module_name + ".py")
92 return module_name, file_name
94 return module_name, file_name
93
95
94 def write_file(self, filename, content):
96 def write_file(self, filename, content):
95 """
97 """
96 Write a file, and force a timestamp difference of at least one second
98 Write a file, and force a timestamp difference of at least one second
97
99
98 Notes
100 Notes
99 -----
101 -----
100 Python's .pyc files record the timestamp of their compilation
102 Python's .pyc files record the timestamp of their compilation
101 with a time resolution of one second.
103 with a time resolution of one second.
102
104
103 Therefore, we need to force a timestamp difference between .py
105 Therefore, we need to force a timestamp difference between .py
104 and .pyc, without having the .py file be timestamped in the
106 and .pyc, without having the .py file be timestamped in the
105 future, and without changing the timestamp of the .pyc file
107 future, and without changing the timestamp of the .pyc file
106 (because that is stored in the file). The only reliable way
108 (because that is stored in the file). The only reliable way
107 to achieve this seems to be to sleep.
109 to achieve this seems to be to sleep.
108 """
110 """
109 content = textwrap.dedent(content)
111 content = textwrap.dedent(content)
110 # Sleep one second + eps
112 # Sleep one second + eps
111 time.sleep(1.05)
113 time.sleep(1.05)
112
114
113 # Write
115 # Write
114 with open(filename, 'w') as f:
116 with open(filename, 'w') as f:
115 f.write(content)
117 f.write(content)
116
118
117 def new_module(self, code):
119 def new_module(self, code):
118 code = textwrap.dedent(code)
120 code = textwrap.dedent(code)
119 mod_name, mod_fn = self.get_module()
121 mod_name, mod_fn = self.get_module()
120 with open(mod_fn, 'w') as f:
122 with open(mod_fn, 'w') as f:
121 f.write(code)
123 f.write(code)
122 return mod_name, mod_fn
124 return mod_name, mod_fn
123
125
124 #-----------------------------------------------------------------------------
126 #-----------------------------------------------------------------------------
125 # Test automatic reloading
127 # Test automatic reloading
126 #-----------------------------------------------------------------------------
128 #-----------------------------------------------------------------------------
127
129
128 def pickle_get_current_class(obj):
130 def pickle_get_current_class(obj):
129 """
131 """
130 Original issue comes from pickle; hence the name.
132 Original issue comes from pickle; hence the name.
131 """
133 """
132 name = obj.__class__.__name__
134 name = obj.__class__.__name__
133 module_name = getattr(obj, "__module__", None)
135 module_name = getattr(obj, "__module__", None)
134 obj2 = sys.modules[module_name]
136 obj2 = sys.modules[module_name]
135 for subpath in name.split("."):
137 for subpath in name.split("."):
136 obj2 = getattr(obj2, subpath)
138 obj2 = getattr(obj2, subpath)
137 return obj2
139 return obj2
138
140
139 class TestAutoreload(Fixture):
141 class TestAutoreload(Fixture):
140
142
141 @skipif(sys.version_info < (3, 6))
143 @skipif(sys.version_info < (3, 6))
142 def test_reload_enums(self):
144 def test_reload_enums(self):
143 import enum
145 import enum
144 mod_name, mod_fn = self.new_module(textwrap.dedent("""
146 mod_name, mod_fn = self.new_module(textwrap.dedent("""
145 from enum import Enum
147 from enum import Enum
146 class MyEnum(Enum):
148 class MyEnum(Enum):
147 A = 'A'
149 A = 'A'
148 B = 'B'
150 B = 'B'
149 """))
151 """))
150 self.shell.magic_autoreload("2")
152 self.shell.magic_autoreload("2")
151 self.shell.magic_aimport(mod_name)
153 self.shell.magic_aimport(mod_name)
152 self.write_file(mod_fn, textwrap.dedent("""
154 self.write_file(mod_fn, textwrap.dedent("""
153 from enum import Enum
155 from enum import Enum
154 class MyEnum(Enum):
156 class MyEnum(Enum):
155 A = 'A'
157 A = 'A'
156 B = 'B'
158 B = 'B'
157 C = 'C'
159 C = 'C'
158 """))
160 """))
159 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
161 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
160 self.shell.run_code("pass") # trigger another reload
162 self.shell.run_code("pass") # trigger another reload
161
163
162 def test_reload_class_type(self):
164 def test_reload_class_type(self):
163 self.shell.magic_autoreload("2")
165 self.shell.magic_autoreload("2")
164 mod_name, mod_fn = self.new_module(
166 mod_name, mod_fn = self.new_module(
165 """
167 """
166 class Test():
168 class Test():
167 def meth(self):
169 def meth(self):
168 return "old"
170 return "old"
169 """
171 """
170 )
172 )
171 assert "test" not in self.shell.ns
173 assert "test" not in self.shell.ns
172 assert "result" not in self.shell.ns
174 assert "result" not in self.shell.ns
173
175
174 self.shell.run_code("from %s import Test" % mod_name)
176 self.shell.run_code("from %s import Test" % mod_name)
175 self.shell.run_code("test = Test()")
177 self.shell.run_code("test = Test()")
176
178
177 self.write_file(
179 self.write_file(
178 mod_fn,
180 mod_fn,
179 """
181 """
180 class Test():
182 class Test():
181 def meth(self):
183 def meth(self):
182 return "new"
184 return "new"
183 """,
185 """,
184 )
186 )
185
187
186 test_object = self.shell.ns["test"]
188 test_object = self.shell.ns["test"]
187
189
188 # important to trigger autoreload logic !
190 # important to trigger autoreload logic !
189 self.shell.run_code("pass")
191 self.shell.run_code("pass")
190
192
191 test_class = pickle_get_current_class(test_object)
193 test_class = pickle_get_current_class(test_object)
192 assert isinstance(test_object, test_class)
194 assert isinstance(test_object, test_class)
193
195
194 # extra check.
196 # extra check.
195 self.shell.run_code("import pickle")
197 self.shell.run_code("import pickle")
196 self.shell.run_code("p = pickle.dumps(test)")
198 self.shell.run_code("p = pickle.dumps(test)")
197
199
198 def test_reload_class_attributes(self):
200 def test_reload_class_attributes(self):
199 self.shell.magic_autoreload("2")
201 self.shell.magic_autoreload("2")
200 mod_name, mod_fn = self.new_module(textwrap.dedent("""
202 mod_name, mod_fn = self.new_module(textwrap.dedent("""
201 class MyClass:
203 class MyClass:
202
204
203 def __init__(self, a=10):
205 def __init__(self, a=10):
204 self.a = a
206 self.a = a
205 self.b = 22
207 self.b = 22
206 # self.toto = 33
208 # self.toto = 33
207
209
208 def square(self):
210 def square(self):
209 print('compute square')
211 print('compute square')
210 return self.a*self.a
212 return self.a*self.a
211 """
213 """
212 )
214 )
213 )
215 )
214 self.shell.run_code("from %s import MyClass" % mod_name)
216 self.shell.run_code("from %s import MyClass" % mod_name)
215 self.shell.run_code("first = MyClass(5)")
217 self.shell.run_code("first = MyClass(5)")
216 self.shell.run_code("first.square()")
218 self.shell.run_code("first.square()")
217 with nt.assert_raises(AttributeError):
219 with nt.assert_raises(AttributeError):
218 self.shell.run_code("first.cube()")
220 self.shell.run_code("first.cube()")
219 with nt.assert_raises(AttributeError):
221 with nt.assert_raises(AttributeError):
220 self.shell.run_code("first.power(5)")
222 self.shell.run_code("first.power(5)")
221 self.shell.run_code("first.b")
223 self.shell.run_code("first.b")
222 with nt.assert_raises(AttributeError):
224 with nt.assert_raises(AttributeError):
223 self.shell.run_code("first.toto")
225 self.shell.run_code("first.toto")
224
226
225 # remove square, add power
227 # remove square, add power
226
228
227 self.write_file(
229 self.write_file(
228 mod_fn,
230 mod_fn,
229 textwrap.dedent(
231 textwrap.dedent(
230 """
232 """
231 class MyClass:
233 class MyClass:
232
234
233 def __init__(self, a=10):
235 def __init__(self, a=10):
234 self.a = a
236 self.a = a
235 self.b = 11
237 self.b = 11
236
238
237 def power(self, p):
239 def power(self, p):
238 print('compute power '+str(p))
240 print('compute power '+str(p))
239 return self.a**p
241 return self.a**p
240 """
242 """
241 ),
243 ),
242 )
244 )
243
245
244 self.shell.run_code("second = MyClass(5)")
246 self.shell.run_code("second = MyClass(5)")
245
247
246 for object_name in {'first', 'second'}:
248 for object_name in {'first', 'second'}:
247 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
249 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
248 with nt.assert_raises(AttributeError):
250 with nt.assert_raises(AttributeError):
249 self.shell.run_code("{object_name}.cube()".format(object_name=object_name))
251 self.shell.run_code("{object_name}.cube()".format(object_name=object_name))
250 with nt.assert_raises(AttributeError):
252 with nt.assert_raises(AttributeError):
251 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
253 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
252 self.shell.run_code("{object_name}.b".format(object_name=object_name))
254 self.shell.run_code("{object_name}.b".format(object_name=object_name))
253 self.shell.run_code("{object_name}.a".format(object_name=object_name))
255 self.shell.run_code("{object_name}.a".format(object_name=object_name))
254 with nt.assert_raises(AttributeError):
256 with nt.assert_raises(AttributeError):
255 self.shell.run_code("{object_name}.toto".format(object_name=object_name))
257 self.shell.run_code("{object_name}.toto".format(object_name=object_name))
256
258
257 def _check_smoketest(self, use_aimport=True):
259 def _check_smoketest(self, use_aimport=True):
258 """
260 """
259 Functional test for the automatic reloader using either
261 Functional test for the automatic reloader using either
260 '%autoreload 1' or '%autoreload 2'
262 '%autoreload 1' or '%autoreload 2'
261 """
263 """
262
264
263 mod_name, mod_fn = self.new_module("""
265 mod_name, mod_fn = self.new_module("""
264 x = 9
266 x = 9
265
267
266 z = 123 # this item will be deleted
268 z = 123 # this item will be deleted
267
269
268 def foo(y):
270 def foo(y):
269 return y + 3
271 return y + 3
270
272
271 class Baz(object):
273 class Baz(object):
272 def __init__(self, x):
274 def __init__(self, x):
273 self.x = x
275 self.x = x
274 def bar(self, y):
276 def bar(self, y):
275 return self.x + y
277 return self.x + y
276 @property
278 @property
277 def quux(self):
279 def quux(self):
278 return 42
280 return 42
279 def zzz(self):
281 def zzz(self):
280 '''This method will be deleted below'''
282 '''This method will be deleted below'''
281 return 99
283 return 99
282
284
283 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
285 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
284 def foo(self):
286 def foo(self):
285 return 1
287 return 1
286 """)
288 """)
287
289
288 #
290 #
289 # Import module, and mark for reloading
291 # Import module, and mark for reloading
290 #
292 #
291 if use_aimport:
293 if use_aimport:
292 self.shell.magic_autoreload("1")
294 self.shell.magic_autoreload("1")
293 self.shell.magic_aimport(mod_name)
295 self.shell.magic_aimport(mod_name)
294 stream = StringIO()
296 stream = StringIO()
295 self.shell.magic_aimport("", stream=stream)
297 self.shell.magic_aimport("", stream=stream)
296 nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
298 nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
297
299
298 with nt.assert_raises(ImportError):
300 with nt.assert_raises(ImportError):
299 self.shell.magic_aimport("tmpmod_as318989e89ds")
301 self.shell.magic_aimport("tmpmod_as318989e89ds")
300 else:
302 else:
301 self.shell.magic_autoreload("2")
303 self.shell.magic_autoreload("2")
302 self.shell.run_code("import %s" % mod_name)
304 self.shell.run_code("import %s" % mod_name)
303 stream = StringIO()
305 stream = StringIO()
304 self.shell.magic_aimport("", stream=stream)
306 self.shell.magic_aimport("", stream=stream)
305 nt.assert_true("Modules to reload:\nall-except-skipped" in
307 nt.assert_true("Modules to reload:\nall-except-skipped" in
306 stream.getvalue())
308 stream.getvalue())
307 nt.assert_in(mod_name, self.shell.ns)
309 nt.assert_in(mod_name, self.shell.ns)
308
310
309 mod = sys.modules[mod_name]
311 mod = sys.modules[mod_name]
310
312
311 #
313 #
312 # Test module contents
314 # Test module contents
313 #
315 #
314 old_foo = mod.foo
316 old_foo = mod.foo
315 old_obj = mod.Baz(9)
317 old_obj = mod.Baz(9)
316 old_obj2 = mod.Bar()
318 old_obj2 = mod.Bar()
317
319
318 def check_module_contents():
320 def check_module_contents():
319 nt.assert_equal(mod.x, 9)
321 nt.assert_equal(mod.x, 9)
320 nt.assert_equal(mod.z, 123)
322 nt.assert_equal(mod.z, 123)
321
323
322 nt.assert_equal(old_foo(0), 3)
324 nt.assert_equal(old_foo(0), 3)
323 nt.assert_equal(mod.foo(0), 3)
325 nt.assert_equal(mod.foo(0), 3)
324
326
325 obj = mod.Baz(9)
327 obj = mod.Baz(9)
326 nt.assert_equal(old_obj.bar(1), 10)
328 nt.assert_equal(old_obj.bar(1), 10)
327 nt.assert_equal(obj.bar(1), 10)
329 nt.assert_equal(obj.bar(1), 10)
328 nt.assert_equal(obj.quux, 42)
330 nt.assert_equal(obj.quux, 42)
329 nt.assert_equal(obj.zzz(), 99)
331 nt.assert_equal(obj.zzz(), 99)
330
332
331 obj2 = mod.Bar()
333 obj2 = mod.Bar()
332 nt.assert_equal(old_obj2.foo(), 1)
334 nt.assert_equal(old_obj2.foo(), 1)
333 nt.assert_equal(obj2.foo(), 1)
335 nt.assert_equal(obj2.foo(), 1)
334
336
335 check_module_contents()
337 check_module_contents()
336
338
337 #
339 #
338 # Simulate a failed reload: no reload should occur and exactly
340 # Simulate a failed reload: no reload should occur and exactly
339 # one error message should be printed
341 # one error message should be printed
340 #
342 #
341 self.write_file(mod_fn, """
343 self.write_file(mod_fn, """
342 a syntax error
344 a syntax error
343 """)
345 """)
344
346
345 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
347 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
346 self.shell.run_code("pass") # trigger reload
348 self.shell.run_code("pass") # trigger reload
347 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
349 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
348 self.shell.run_code("pass") # trigger another reload
350 self.shell.run_code("pass") # trigger another reload
349 check_module_contents()
351 check_module_contents()
350
352
351 #
353 #
352 # Rewrite module (this time reload should succeed)
354 # Rewrite module (this time reload should succeed)
353 #
355 #
354 self.write_file(mod_fn, """
356 self.write_file(mod_fn, """
355 x = 10
357 x = 10
356
358
357 def foo(y):
359 def foo(y):
358 return y + 4
360 return y + 4
359
361
360 class Baz(object):
362 class Baz(object):
361 def __init__(self, x):
363 def __init__(self, x):
362 self.x = x
364 self.x = x
363 def bar(self, y):
365 def bar(self, y):
364 return self.x + y + 1
366 return self.x + y + 1
365 @property
367 @property
366 def quux(self):
368 def quux(self):
367 return 43
369 return 43
368
370
369 class Bar: # old-style class
371 class Bar: # old-style class
370 def foo(self):
372 def foo(self):
371 return 2
373 return 2
372 """)
374 """)
373
375
374 def check_module_contents():
376 def check_module_contents():
375 nt.assert_equal(mod.x, 10)
377 nt.assert_equal(mod.x, 10)
376 nt.assert_false(hasattr(mod, 'z'))
378 nt.assert_false(hasattr(mod, 'z'))
377
379
378 nt.assert_equal(old_foo(0), 4) # superreload magic!
380 nt.assert_equal(old_foo(0), 4) # superreload magic!
379 nt.assert_equal(mod.foo(0), 4)
381 nt.assert_equal(mod.foo(0), 4)
380
382
381 obj = mod.Baz(9)
383 obj = mod.Baz(9)
382 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
384 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
383 nt.assert_equal(obj.bar(1), 11)
385 nt.assert_equal(obj.bar(1), 11)
384
386
385 nt.assert_equal(old_obj.quux, 43)
387 nt.assert_equal(old_obj.quux, 43)
386 nt.assert_equal(obj.quux, 43)
388 nt.assert_equal(obj.quux, 43)
387
389
388 nt.assert_false(hasattr(old_obj, 'zzz'))
390 nt.assert_false(hasattr(old_obj, 'zzz'))
389 nt.assert_false(hasattr(obj, 'zzz'))
391 nt.assert_false(hasattr(obj, 'zzz'))
390
392
391 obj2 = mod.Bar()
393 obj2 = mod.Bar()
392 nt.assert_equal(old_obj2.foo(), 2)
394 nt.assert_equal(old_obj2.foo(), 2)
393 nt.assert_equal(obj2.foo(), 2)
395 nt.assert_equal(obj2.foo(), 2)
394
396
395 self.shell.run_code("pass") # trigger reload
397 self.shell.run_code("pass") # trigger reload
396 check_module_contents()
398 check_module_contents()
397
399
398 #
400 #
399 # Another failure case: deleted file (shouldn't reload)
401 # Another failure case: deleted file (shouldn't reload)
400 #
402 #
401 os.unlink(mod_fn)
403 os.unlink(mod_fn)
402
404
403 self.shell.run_code("pass") # trigger reload
405 self.shell.run_code("pass") # trigger reload
404 check_module_contents()
406 check_module_contents()
405
407
406 #
408 #
407 # Disable autoreload and rewrite module: no reload should occur
409 # Disable autoreload and rewrite module: no reload should occur
408 #
410 #
409 if use_aimport:
411 if use_aimport:
410 self.shell.magic_aimport("-" + mod_name)
412 self.shell.magic_aimport("-" + mod_name)
411 stream = StringIO()
413 stream = StringIO()
412 self.shell.magic_aimport("", stream=stream)
414 self.shell.magic_aimport("", stream=stream)
413 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
415 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
414 stream.getvalue())
416 stream.getvalue())
415
417
416 # This should succeed, although no such module exists
418 # This should succeed, although no such module exists
417 self.shell.magic_aimport("-tmpmod_as318989e89ds")
419 self.shell.magic_aimport("-tmpmod_as318989e89ds")
418 else:
420 else:
419 self.shell.magic_autoreload("0")
421 self.shell.magic_autoreload("0")
420
422
421 self.write_file(mod_fn, """
423 self.write_file(mod_fn, """
422 x = -99
424 x = -99
423 """)
425 """)
424
426
425 self.shell.run_code("pass") # trigger reload
427 self.shell.run_code("pass") # trigger reload
426 self.shell.run_code("pass")
428 self.shell.run_code("pass")
427 check_module_contents()
429 check_module_contents()
428
430
429 #
431 #
430 # Re-enable autoreload: reload should now occur
432 # Re-enable autoreload: reload should now occur
431 #
433 #
432 if use_aimport:
434 if use_aimport:
433 self.shell.magic_aimport(mod_name)
435 self.shell.magic_aimport(mod_name)
434 else:
436 else:
435 self.shell.magic_autoreload("")
437 self.shell.magic_autoreload("")
436
438
437 self.shell.run_code("pass") # trigger reload
439 self.shell.run_code("pass") # trigger reload
438 nt.assert_equal(mod.x, -99)
440 nt.assert_equal(mod.x, -99)
439
441
440 def test_smoketest_aimport(self):
442 def test_smoketest_aimport(self):
441 self._check_smoketest(use_aimport=True)
443 self._check_smoketest(use_aimport=True)
442
444
443 def test_smoketest_autoreload(self):
445 def test_smoketest_autoreload(self):
444 self._check_smoketest(use_aimport=False)
446 self._check_smoketest(use_aimport=False)
445
447
446
448
447
449
448
450
449
451
@@ -1,135 +1,135 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for testing.tools
3 Tests for testing.tools
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
17 import os
18 import unittest
18 import unittest
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
23 from IPython.testing import tools as tt
23 from IPython.testing import tools as tt
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Tests
26 # Tests
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 @dec.skip_win32
29 @dec.skip_win32
30 def test_full_path_posix():
30 def test_full_path_posix():
31 spath = '/foo/bar.py'
31 spath = '/foo/bar.py'
32 result = tt.full_path(spath,['a.txt','b.txt'])
32 result = tt.full_path(spath,['a.txt','b.txt'])
33 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
33 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
34 spath = '/foo'
34 spath = '/foo'
35 result = tt.full_path(spath,['a.txt','b.txt'])
35 result = tt.full_path(spath,['a.txt','b.txt'])
36 nt.assert_equal(result, ['/a.txt', '/b.txt'])
36 nt.assert_equal(result, ['/a.txt', '/b.txt'])
37 result = tt.full_path(spath,'a.txt')
37 result = tt.full_path(spath,'a.txt')
38 nt.assert_equal(result, ['/a.txt'])
38 nt.assert_equal(result, ['/a.txt'])
39
39
40
40
41 @dec.skip_if_not_win32
41 @dec.skip_if_not_win32
42 def test_full_path_win32():
42 def test_full_path_win32():
43 spath = 'c:\\foo\\bar.py'
43 spath = 'c:\\foo\\bar.py'
44 result = tt.full_path(spath,['a.txt','b.txt'])
44 result = tt.full_path(spath,['a.txt','b.txt'])
45 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
45 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
46 spath = 'c:\\foo'
46 spath = 'c:\\foo'
47 result = tt.full_path(spath,['a.txt','b.txt'])
47 result = tt.full_path(spath,['a.txt','b.txt'])
48 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
48 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
49 result = tt.full_path(spath,'a.txt')
49 result = tt.full_path(spath,'a.txt')
50 nt.assert_equal(result, ['c:\\a.txt'])
50 nt.assert_equal(result, ['c:\\a.txt'])
51
51
52
52
53 def test_parser():
53 def test_parser():
54 err = ("FAILED (errors=1)", 1, 0)
54 err = ("FAILED (errors=1)", 1, 0)
55 fail = ("FAILED (failures=1)", 0, 1)
55 fail = ("FAILED (failures=1)", 0, 1)
56 both = ("FAILED (errors=1, failures=1)", 1, 1)
56 both = ("FAILED (errors=1, failures=1)", 1, 1)
57 for txt, nerr, nfail in [err, fail, both]:
57 for txt, nerr, nfail in [err, fail, both]:
58 nerr1, nfail1 = tt.parse_test_output(txt)
58 nerr1, nfail1 = tt.parse_test_output(txt)
59 nt.assert_equal(nerr, nerr1)
59 nt.assert_equal(nerr, nerr1)
60 nt.assert_equal(nfail, nfail1)
60 nt.assert_equal(nfail, nfail1)
61
61
62
62
63 def test_temp_pyfile():
63 def test_temp_pyfile():
64 src = 'pass\n'
64 src = 'pass\n'
65 fname = tt.temp_pyfile(src)
65 fname = tt.temp_pyfile(src)
66 assert os.path.isfile(fname)
66 assert os.path.isfile(fname)
67 with open(fname) as fh2:
67 with open(fname) as fh2:
68 src2 = fh2.read()
68 src2 = fh2.read()
69 nt.assert_equal(src2, src)
69 nt.assert_equal(src2, src)
70
70
71 class TestAssertPrints(unittest.TestCase):
71 class TestAssertPrints(unittest.TestCase):
72 def test_passing(self):
72 def test_passing(self):
73 with tt.AssertPrints("abc"):
73 with tt.AssertPrints("abc"):
74 print("abcd")
74 print("abcd")
75 print("def")
75 print("def")
76 print(b"ghi")
76 print(b"ghi")
77
77
78 def test_failing(self):
78 def test_failing(self):
79 def func():
79 def func():
80 with tt.AssertPrints("abc"):
80 with tt.AssertPrints("abc"):
81 print("acd")
81 print("acd")
82 print("def")
82 print("def")
83 print(b"ghi")
83 print(b"ghi")
84
84
85 self.assertRaises(AssertionError, func)
85 self.assertRaises(AssertionError, func)
86
86
87
87
88 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
88 class Test_ipexec_validate(tt.TempFileMixin):
89 def test_main_path(self):
89 def test_main_path(self):
90 """Test with only stdout results.
90 """Test with only stdout results.
91 """
91 """
92 self.mktmp("print('A')\n"
92 self.mktmp("print('A')\n"
93 "print('B')\n"
93 "print('B')\n"
94 )
94 )
95 out = "A\nB"
95 out = "A\nB"
96 tt.ipexec_validate(self.fname, out)
96 tt.ipexec_validate(self.fname, out)
97
97
98 def test_main_path2(self):
98 def test_main_path2(self):
99 """Test with only stdout results, expecting windows line endings.
99 """Test with only stdout results, expecting windows line endings.
100 """
100 """
101 self.mktmp("print('A')\n"
101 self.mktmp("print('A')\n"
102 "print('B')\n"
102 "print('B')\n"
103 )
103 )
104 out = "A\r\nB"
104 out = "A\r\nB"
105 tt.ipexec_validate(self.fname, out)
105 tt.ipexec_validate(self.fname, out)
106
106
107 def test_exception_path(self):
107 def test_exception_path(self):
108 """Test exception path in exception_validate.
108 """Test exception path in exception_validate.
109 """
109 """
110 self.mktmp("import sys\n"
110 self.mktmp("import sys\n"
111 "print('A')\n"
111 "print('A')\n"
112 "print('B')\n"
112 "print('B')\n"
113 "print('C', file=sys.stderr)\n"
113 "print('C', file=sys.stderr)\n"
114 "print('D', file=sys.stderr)\n"
114 "print('D', file=sys.stderr)\n"
115 )
115 )
116 out = "A\nB"
116 out = "A\nB"
117 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
117 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
118
118
119 def test_exception_path2(self):
119 def test_exception_path2(self):
120 """Test exception path in exception_validate, expecting windows line endings.
120 """Test exception path in exception_validate, expecting windows line endings.
121 """
121 """
122 self.mktmp("import sys\n"
122 self.mktmp("import sys\n"
123 "print('A')\n"
123 "print('A')\n"
124 "print('B')\n"
124 "print('B')\n"
125 "print('C', file=sys.stderr)\n"
125 "print('C', file=sys.stderr)\n"
126 "print('D', file=sys.stderr)\n"
126 "print('D', file=sys.stderr)\n"
127 )
127 )
128 out = "A\r\nB"
128 out = "A\r\nB"
129 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
129 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
130
130
131
131
132 def tearDown(self):
132 def tearDown(self):
133 # tear down correctly the mixin,
133 # tear down correctly the mixin,
134 # unittest.TestCase.tearDown does nothing
134 # unittest.TestCase.tearDown does nothing
135 tt.TempFileMixin.tearDown(self)
135 tt.TempFileMixin.tearDown(self)
@@ -1,470 +1,471 b''
1 """Generic testing tools.
1 """Generic testing tools.
2
2
3 Authors
3 Authors
4 -------
4 -------
5 - Fernando Perez <Fernando.Perez@berkeley.edu>
5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 """
6 """
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 os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15 import tempfile
15 import tempfile
16 import unittest
16
17
17 from contextlib import contextmanager
18 from contextlib import contextmanager
18 from io import StringIO
19 from io import StringIO
19 from subprocess import Popen, PIPE
20 from subprocess import Popen, PIPE
20 from unittest.mock import patch
21 from unittest.mock import patch
21
22
22 try:
23 try:
23 # These tools are used by parts of the runtime, so we make the nose
24 # These tools are used by parts of the runtime, so we make the nose
24 # dependency optional at this point. Nose is a hard dependency to run the
25 # dependency optional at this point. Nose is a hard dependency to run the
25 # test suite, but NOT to use ipython itself.
26 # test suite, but NOT to use ipython itself.
26 import nose.tools as nt
27 import nose.tools as nt
27 has_nose = True
28 has_nose = True
28 except ImportError:
29 except ImportError:
29 has_nose = False
30 has_nose = False
30
31
31 from traitlets.config.loader import Config
32 from traitlets.config.loader import Config
32 from IPython.utils.process import get_output_error_code
33 from IPython.utils.process import get_output_error_code
33 from IPython.utils.text import list_strings
34 from IPython.utils.text import list_strings
34 from IPython.utils.io import temp_pyfile, Tee
35 from IPython.utils.io import temp_pyfile, Tee
35 from IPython.utils import py3compat
36 from IPython.utils import py3compat
36
37
37 from . import decorators as dec
38 from . import decorators as dec
38 from . import skipdoctest
39 from . import skipdoctest
39
40
40
41
41 # The docstring for full_path doctests differently on win32 (different path
42 # The docstring for full_path doctests differently on win32 (different path
42 # separator) so just skip the doctest there. The example remains informative.
43 # separator) so just skip the doctest there. The example remains informative.
43 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
44 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
44
45
45 @doctest_deco
46 @doctest_deco
46 def full_path(startPath,files):
47 def full_path(startPath,files):
47 """Make full paths for all the listed files, based on startPath.
48 """Make full paths for all the listed files, based on startPath.
48
49
49 Only the base part of startPath is kept, since this routine is typically
50 Only the base part of startPath is kept, since this routine is typically
50 used with a script's ``__file__`` variable as startPath. The base of startPath
51 used with a script's ``__file__`` variable as startPath. The base of startPath
51 is then prepended to all the listed files, forming the output list.
52 is then prepended to all the listed files, forming the output list.
52
53
53 Parameters
54 Parameters
54 ----------
55 ----------
55 startPath : string
56 startPath : string
56 Initial path to use as the base for the results. This path is split
57 Initial path to use as the base for the results. This path is split
57 using os.path.split() and only its first component is kept.
58 using os.path.split() and only its first component is kept.
58
59
59 files : string or list
60 files : string or list
60 One or more files.
61 One or more files.
61
62
62 Examples
63 Examples
63 --------
64 --------
64
65
65 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
66 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
66 ['/foo/a.txt', '/foo/b.txt']
67 ['/foo/a.txt', '/foo/b.txt']
67
68
68 >>> full_path('/foo',['a.txt','b.txt'])
69 >>> full_path('/foo',['a.txt','b.txt'])
69 ['/a.txt', '/b.txt']
70 ['/a.txt', '/b.txt']
70
71
71 If a single file is given, the output is still a list::
72 If a single file is given, the output is still a list::
72
73
73 >>> full_path('/foo','a.txt')
74 >>> full_path('/foo','a.txt')
74 ['/a.txt']
75 ['/a.txt']
75 """
76 """
76
77
77 files = list_strings(files)
78 files = list_strings(files)
78 base = os.path.split(startPath)[0]
79 base = os.path.split(startPath)[0]
79 return [ os.path.join(base,f) for f in files ]
80 return [ os.path.join(base,f) for f in files ]
80
81
81
82
82 def parse_test_output(txt):
83 def parse_test_output(txt):
83 """Parse the output of a test run and return errors, failures.
84 """Parse the output of a test run and return errors, failures.
84
85
85 Parameters
86 Parameters
86 ----------
87 ----------
87 txt : str
88 txt : str
88 Text output of a test run, assumed to contain a line of one of the
89 Text output of a test run, assumed to contain a line of one of the
89 following forms::
90 following forms::
90
91
91 'FAILED (errors=1)'
92 'FAILED (errors=1)'
92 'FAILED (failures=1)'
93 'FAILED (failures=1)'
93 'FAILED (errors=1, failures=1)'
94 'FAILED (errors=1, failures=1)'
94
95
95 Returns
96 Returns
96 -------
97 -------
97 nerr, nfail
98 nerr, nfail
98 number of errors and failures.
99 number of errors and failures.
99 """
100 """
100
101
101 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
102 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
102 if err_m:
103 if err_m:
103 nerr = int(err_m.group(1))
104 nerr = int(err_m.group(1))
104 nfail = 0
105 nfail = 0
105 return nerr, nfail
106 return nerr, nfail
106
107
107 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
108 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
108 if fail_m:
109 if fail_m:
109 nerr = 0
110 nerr = 0
110 nfail = int(fail_m.group(1))
111 nfail = int(fail_m.group(1))
111 return nerr, nfail
112 return nerr, nfail
112
113
113 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
114 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
114 re.MULTILINE)
115 re.MULTILINE)
115 if both_m:
116 if both_m:
116 nerr = int(both_m.group(1))
117 nerr = int(both_m.group(1))
117 nfail = int(both_m.group(2))
118 nfail = int(both_m.group(2))
118 return nerr, nfail
119 return nerr, nfail
119
120
120 # If the input didn't match any of these forms, assume no error/failures
121 # If the input didn't match any of these forms, assume no error/failures
121 return 0, 0
122 return 0, 0
122
123
123
124
124 # So nose doesn't think this is a test
125 # So nose doesn't think this is a test
125 parse_test_output.__test__ = False
126 parse_test_output.__test__ = False
126
127
127
128
128 def default_argv():
129 def default_argv():
129 """Return a valid default argv for creating testing instances of ipython"""
130 """Return a valid default argv for creating testing instances of ipython"""
130
131
131 return ['--quick', # so no config file is loaded
132 return ['--quick', # so no config file is loaded
132 # Other defaults to minimize side effects on stdout
133 # Other defaults to minimize side effects on stdout
133 '--colors=NoColor', '--no-term-title','--no-banner',
134 '--colors=NoColor', '--no-term-title','--no-banner',
134 '--autocall=0']
135 '--autocall=0']
135
136
136
137
137 def default_config():
138 def default_config():
138 """Return a config object with good defaults for testing."""
139 """Return a config object with good defaults for testing."""
139 config = Config()
140 config = Config()
140 config.TerminalInteractiveShell.colors = 'NoColor'
141 config.TerminalInteractiveShell.colors = 'NoColor'
141 config.TerminalTerminalInteractiveShell.term_title = False,
142 config.TerminalTerminalInteractiveShell.term_title = False,
142 config.TerminalInteractiveShell.autocall = 0
143 config.TerminalInteractiveShell.autocall = 0
143 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
144 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
144 config.HistoryManager.hist_file = f.name
145 config.HistoryManager.hist_file = f.name
145 f.close()
146 f.close()
146 config.HistoryManager.db_cache_size = 10000
147 config.HistoryManager.db_cache_size = 10000
147 return config
148 return config
148
149
149
150
150 def get_ipython_cmd(as_string=False):
151 def get_ipython_cmd(as_string=False):
151 """
152 """
152 Return appropriate IPython command line name. By default, this will return
153 Return appropriate IPython command line name. By default, this will return
153 a list that can be used with subprocess.Popen, for example, but passing
154 a list that can be used with subprocess.Popen, for example, but passing
154 `as_string=True` allows for returning the IPython command as a string.
155 `as_string=True` allows for returning the IPython command as a string.
155
156
156 Parameters
157 Parameters
157 ----------
158 ----------
158 as_string: bool
159 as_string: bool
159 Flag to allow to return the command as a string.
160 Flag to allow to return the command as a string.
160 """
161 """
161 ipython_cmd = [sys.executable, "-m", "IPython"]
162 ipython_cmd = [sys.executable, "-m", "IPython"]
162
163
163 if as_string:
164 if as_string:
164 ipython_cmd = " ".join(ipython_cmd)
165 ipython_cmd = " ".join(ipython_cmd)
165
166
166 return ipython_cmd
167 return ipython_cmd
167
168
168 def ipexec(fname, options=None, commands=()):
169 def ipexec(fname, options=None, commands=()):
169 """Utility to call 'ipython filename'.
170 """Utility to call 'ipython filename'.
170
171
171 Starts IPython with a minimal and safe configuration to make startup as fast
172 Starts IPython with a minimal and safe configuration to make startup as fast
172 as possible.
173 as possible.
173
174
174 Note that this starts IPython in a subprocess!
175 Note that this starts IPython in a subprocess!
175
176
176 Parameters
177 Parameters
177 ----------
178 ----------
178 fname : str
179 fname : str
179 Name of file to be executed (should have .py or .ipy extension).
180 Name of file to be executed (should have .py or .ipy extension).
180
181
181 options : optional, list
182 options : optional, list
182 Extra command-line flags to be passed to IPython.
183 Extra command-line flags to be passed to IPython.
183
184
184 commands : optional, list
185 commands : optional, list
185 Commands to send in on stdin
186 Commands to send in on stdin
186
187
187 Returns
188 Returns
188 -------
189 -------
189 ``(stdout, stderr)`` of ipython subprocess.
190 ``(stdout, stderr)`` of ipython subprocess.
190 """
191 """
191 if options is None: options = []
192 if options is None: options = []
192
193
193 cmdargs = default_argv() + options
194 cmdargs = default_argv() + options
194
195
195 test_dir = os.path.dirname(__file__)
196 test_dir = os.path.dirname(__file__)
196
197
197 ipython_cmd = get_ipython_cmd()
198 ipython_cmd = get_ipython_cmd()
198 # Absolute path for filename
199 # Absolute path for filename
199 full_fname = os.path.join(test_dir, fname)
200 full_fname = os.path.join(test_dir, fname)
200 full_cmd = ipython_cmd + cmdargs + [full_fname]
201 full_cmd = ipython_cmd + cmdargs + [full_fname]
201 env = os.environ.copy()
202 env = os.environ.copy()
202 # FIXME: ignore all warnings in ipexec while we have shims
203 # FIXME: ignore all warnings in ipexec while we have shims
203 # should we keep suppressing warnings here, even after removing shims?
204 # should we keep suppressing warnings here, even after removing shims?
204 env['PYTHONWARNINGS'] = 'ignore'
205 env['PYTHONWARNINGS'] = 'ignore'
205 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
206 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
206 for k, v in env.items():
207 for k, v in env.items():
207 # Debug a bizarre failure we've seen on Windows:
208 # Debug a bizarre failure we've seen on Windows:
208 # TypeError: environment can only contain strings
209 # TypeError: environment can only contain strings
209 if not isinstance(v, str):
210 if not isinstance(v, str):
210 print(k, v)
211 print(k, v)
211 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
212 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
212 out, err = p.communicate(input=py3compat.encode('\n'.join(commands)) or None)
213 out, err = p.communicate(input=py3compat.encode('\n'.join(commands)) or None)
213 out, err = py3compat.decode(out), py3compat.decode(err)
214 out, err = py3compat.decode(out), py3compat.decode(err)
214 # `import readline` causes 'ESC[?1034h' to be output sometimes,
215 # `import readline` causes 'ESC[?1034h' to be output sometimes,
215 # so strip that out before doing comparisons
216 # so strip that out before doing comparisons
216 if out:
217 if out:
217 out = re.sub(r'\x1b\[[^h]+h', '', out)
218 out = re.sub(r'\x1b\[[^h]+h', '', out)
218 return out, err
219 return out, err
219
220
220
221
221 def ipexec_validate(fname, expected_out, expected_err='',
222 def ipexec_validate(fname, expected_out, expected_err='',
222 options=None, commands=()):
223 options=None, commands=()):
223 """Utility to call 'ipython filename' and validate output/error.
224 """Utility to call 'ipython filename' and validate output/error.
224
225
225 This function raises an AssertionError if the validation fails.
226 This function raises an AssertionError if the validation fails.
226
227
227 Note that this starts IPython in a subprocess!
228 Note that this starts IPython in a subprocess!
228
229
229 Parameters
230 Parameters
230 ----------
231 ----------
231 fname : str
232 fname : str
232 Name of the file to be executed (should have .py or .ipy extension).
233 Name of the file to be executed (should have .py or .ipy extension).
233
234
234 expected_out : str
235 expected_out : str
235 Expected stdout of the process.
236 Expected stdout of the process.
236
237
237 expected_err : optional, str
238 expected_err : optional, str
238 Expected stderr of the process.
239 Expected stderr of the process.
239
240
240 options : optional, list
241 options : optional, list
241 Extra command-line flags to be passed to IPython.
242 Extra command-line flags to be passed to IPython.
242
243
243 Returns
244 Returns
244 -------
245 -------
245 None
246 None
246 """
247 """
247
248
248 import nose.tools as nt
249 import nose.tools as nt
249
250
250 out, err = ipexec(fname, options, commands)
251 out, err = ipexec(fname, options, commands)
251 #print 'OUT', out # dbg
252 #print 'OUT', out # dbg
252 #print 'ERR', err # dbg
253 #print 'ERR', err # dbg
253 # If there are any errors, we must check those before stdout, as they may be
254 # If there are any errors, we must check those before stdout, as they may be
254 # more informative than simply having an empty stdout.
255 # more informative than simply having an empty stdout.
255 if err:
256 if err:
256 if expected_err:
257 if expected_err:
257 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
258 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
258 else:
259 else:
259 raise ValueError('Running file %r produced error: %r' %
260 raise ValueError('Running file %r produced error: %r' %
260 (fname, err))
261 (fname, err))
261 # If no errors or output on stderr was expected, match stdout
262 # If no errors or output on stderr was expected, match stdout
262 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
263 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
263
264
264
265
265 class TempFileMixin(object):
266 class TempFileMixin(unittest.TestCase):
266 """Utility class to create temporary Python/IPython files.
267 """Utility class to create temporary Python/IPython files.
267
268
268 Meant as a mixin class for test cases."""
269 Meant as a mixin class for test cases."""
269
270
270 def mktmp(self, src, ext='.py'):
271 def mktmp(self, src, ext='.py'):
271 """Make a valid python temp file."""
272 """Make a valid python temp file."""
272 fname = temp_pyfile(src, ext)
273 fname = temp_pyfile(src, ext)
273 if not hasattr(self, 'tmps'):
274 if not hasattr(self, 'tmps'):
274 self.tmps=[]
275 self.tmps=[]
275 self.tmps.append(fname)
276 self.tmps.append(fname)
276 self.fname = fname
277 self.fname = fname
277
278
278 def tearDown(self):
279 def tearDown(self):
279 # If the tmpfile wasn't made because of skipped tests, like in
280 # If the tmpfile wasn't made because of skipped tests, like in
280 # win32, there's nothing to cleanup.
281 # win32, there's nothing to cleanup.
281 if hasattr(self, 'tmps'):
282 if hasattr(self, 'tmps'):
282 for fname in self.tmps:
283 for fname in self.tmps:
283 # If the tmpfile wasn't made because of skipped tests, like in
284 # If the tmpfile wasn't made because of skipped tests, like in
284 # win32, there's nothing to cleanup.
285 # win32, there's nothing to cleanup.
285 try:
286 try:
286 os.unlink(fname)
287 os.unlink(fname)
287 except:
288 except:
288 # On Windows, even though we close the file, we still can't
289 # On Windows, even though we close the file, we still can't
289 # delete it. I have no clue why
290 # delete it. I have no clue why
290 pass
291 pass
291
292
292 def __enter__(self):
293 def __enter__(self):
293 return self
294 return self
294
295
295 def __exit__(self, exc_type, exc_value, traceback):
296 def __exit__(self, exc_type, exc_value, traceback):
296 self.tearDown()
297 self.tearDown()
297
298
298
299
299 pair_fail_msg = ("Testing {0}\n\n"
300 pair_fail_msg = ("Testing {0}\n\n"
300 "In:\n"
301 "In:\n"
301 " {1!r}\n"
302 " {1!r}\n"
302 "Expected:\n"
303 "Expected:\n"
303 " {2!r}\n"
304 " {2!r}\n"
304 "Got:\n"
305 "Got:\n"
305 " {3!r}\n")
306 " {3!r}\n")
306 def check_pairs(func, pairs):
307 def check_pairs(func, pairs):
307 """Utility function for the common case of checking a function with a
308 """Utility function for the common case of checking a function with a
308 sequence of input/output pairs.
309 sequence of input/output pairs.
309
310
310 Parameters
311 Parameters
311 ----------
312 ----------
312 func : callable
313 func : callable
313 The function to be tested. Should accept a single argument.
314 The function to be tested. Should accept a single argument.
314 pairs : iterable
315 pairs : iterable
315 A list of (input, expected_output) tuples.
316 A list of (input, expected_output) tuples.
316
317
317 Returns
318 Returns
318 -------
319 -------
319 None. Raises an AssertionError if any output does not match the expected
320 None. Raises an AssertionError if any output does not match the expected
320 value.
321 value.
321 """
322 """
322 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
323 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
323 for inp, expected in pairs:
324 for inp, expected in pairs:
324 out = func(inp)
325 out = func(inp)
325 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
326 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
326
327
327
328
328 MyStringIO = StringIO
329 MyStringIO = StringIO
329
330
330 _re_type = type(re.compile(r''))
331 _re_type = type(re.compile(r''))
331
332
332 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
333 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
333 -------
334 -------
334 {2!s}
335 {2!s}
335 -------
336 -------
336 """
337 """
337
338
338 class AssertPrints(object):
339 class AssertPrints(object):
339 """Context manager for testing that code prints certain text.
340 """Context manager for testing that code prints certain text.
340
341
341 Examples
342 Examples
342 --------
343 --------
343 >>> with AssertPrints("abc", suppress=False):
344 >>> with AssertPrints("abc", suppress=False):
344 ... print("abcd")
345 ... print("abcd")
345 ... print("def")
346 ... print("def")
346 ...
347 ...
347 abcd
348 abcd
348 def
349 def
349 """
350 """
350 def __init__(self, s, channel='stdout', suppress=True):
351 def __init__(self, s, channel='stdout', suppress=True):
351 self.s = s
352 self.s = s
352 if isinstance(self.s, (str, _re_type)):
353 if isinstance(self.s, (str, _re_type)):
353 self.s = [self.s]
354 self.s = [self.s]
354 self.channel = channel
355 self.channel = channel
355 self.suppress = suppress
356 self.suppress = suppress
356
357
357 def __enter__(self):
358 def __enter__(self):
358 self.orig_stream = getattr(sys, self.channel)
359 self.orig_stream = getattr(sys, self.channel)
359 self.buffer = MyStringIO()
360 self.buffer = MyStringIO()
360 self.tee = Tee(self.buffer, channel=self.channel)
361 self.tee = Tee(self.buffer, channel=self.channel)
361 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
362 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
362
363
363 def __exit__(self, etype, value, traceback):
364 def __exit__(self, etype, value, traceback):
364 try:
365 try:
365 if value is not None:
366 if value is not None:
366 # If an error was raised, don't check anything else
367 # If an error was raised, don't check anything else
367 return False
368 return False
368 self.tee.flush()
369 self.tee.flush()
369 setattr(sys, self.channel, self.orig_stream)
370 setattr(sys, self.channel, self.orig_stream)
370 printed = self.buffer.getvalue()
371 printed = self.buffer.getvalue()
371 for s in self.s:
372 for s in self.s:
372 if isinstance(s, _re_type):
373 if isinstance(s, _re_type):
373 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
374 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
374 else:
375 else:
375 assert s in printed, notprinted_msg.format(s, self.channel, printed)
376 assert s in printed, notprinted_msg.format(s, self.channel, printed)
376 return False
377 return False
377 finally:
378 finally:
378 self.tee.close()
379 self.tee.close()
379
380
380 printed_msg = """Found {0!r} in printed output (on {1}):
381 printed_msg = """Found {0!r} in printed output (on {1}):
381 -------
382 -------
382 {2!s}
383 {2!s}
383 -------
384 -------
384 """
385 """
385
386
386 class AssertNotPrints(AssertPrints):
387 class AssertNotPrints(AssertPrints):
387 """Context manager for checking that certain output *isn't* produced.
388 """Context manager for checking that certain output *isn't* produced.
388
389
389 Counterpart of AssertPrints"""
390 Counterpart of AssertPrints"""
390 def __exit__(self, etype, value, traceback):
391 def __exit__(self, etype, value, traceback):
391 try:
392 try:
392 if value is not None:
393 if value is not None:
393 # If an error was raised, don't check anything else
394 # If an error was raised, don't check anything else
394 self.tee.close()
395 self.tee.close()
395 return False
396 return False
396 self.tee.flush()
397 self.tee.flush()
397 setattr(sys, self.channel, self.orig_stream)
398 setattr(sys, self.channel, self.orig_stream)
398 printed = self.buffer.getvalue()
399 printed = self.buffer.getvalue()
399 for s in self.s:
400 for s in self.s:
400 if isinstance(s, _re_type):
401 if isinstance(s, _re_type):
401 assert not s.search(printed),printed_msg.format(
402 assert not s.search(printed),printed_msg.format(
402 s.pattern, self.channel, printed)
403 s.pattern, self.channel, printed)
403 else:
404 else:
404 assert s not in printed, printed_msg.format(
405 assert s not in printed, printed_msg.format(
405 s, self.channel, printed)
406 s, self.channel, printed)
406 return False
407 return False
407 finally:
408 finally:
408 self.tee.close()
409 self.tee.close()
409
410
410 @contextmanager
411 @contextmanager
411 def mute_warn():
412 def mute_warn():
412 from IPython.utils import warn
413 from IPython.utils import warn
413 save_warn = warn.warn
414 save_warn = warn.warn
414 warn.warn = lambda *a, **kw: None
415 warn.warn = lambda *a, **kw: None
415 try:
416 try:
416 yield
417 yield
417 finally:
418 finally:
418 warn.warn = save_warn
419 warn.warn = save_warn
419
420
420 @contextmanager
421 @contextmanager
421 def make_tempfile(name):
422 def make_tempfile(name):
422 """ Create an empty, named, temporary file for the duration of the context.
423 """ Create an empty, named, temporary file for the duration of the context.
423 """
424 """
424 open(name, 'w').close()
425 open(name, 'w').close()
425 try:
426 try:
426 yield
427 yield
427 finally:
428 finally:
428 os.unlink(name)
429 os.unlink(name)
429
430
430 def fake_input(inputs):
431 def fake_input(inputs):
431 """Temporarily replace the input() function to return the given values
432 """Temporarily replace the input() function to return the given values
432
433
433 Use as a context manager:
434 Use as a context manager:
434
435
435 with fake_input(['result1', 'result2']):
436 with fake_input(['result1', 'result2']):
436 ...
437 ...
437
438
438 Values are returned in order. If input() is called again after the last value
439 Values are returned in order. If input() is called again after the last value
439 was used, EOFError is raised.
440 was used, EOFError is raised.
440 """
441 """
441 it = iter(inputs)
442 it = iter(inputs)
442 def mock_input(prompt=''):
443 def mock_input(prompt=''):
443 try:
444 try:
444 return next(it)
445 return next(it)
445 except StopIteration:
446 except StopIteration:
446 raise EOFError('No more inputs given')
447 raise EOFError('No more inputs given')
447
448
448 return patch('builtins.input', mock_input)
449 return patch('builtins.input', mock_input)
449
450
450 def help_output_test(subcommand=''):
451 def help_output_test(subcommand=''):
451 """test that `ipython [subcommand] -h` works"""
452 """test that `ipython [subcommand] -h` works"""
452 cmd = get_ipython_cmd() + [subcommand, '-h']
453 cmd = get_ipython_cmd() + [subcommand, '-h']
453 out, err, rc = get_output_error_code(cmd)
454 out, err, rc = get_output_error_code(cmd)
454 nt.assert_equal(rc, 0, err)
455 nt.assert_equal(rc, 0, err)
455 nt.assert_not_in("Traceback", err)
456 nt.assert_not_in("Traceback", err)
456 nt.assert_in("Options", out)
457 nt.assert_in("Options", out)
457 nt.assert_in("--help-all", out)
458 nt.assert_in("--help-all", out)
458 return out, err
459 return out, err
459
460
460
461
461 def help_all_output_test(subcommand=''):
462 def help_all_output_test(subcommand=''):
462 """test that `ipython [subcommand] --help-all` works"""
463 """test that `ipython [subcommand] --help-all` works"""
463 cmd = get_ipython_cmd() + [subcommand, '--help-all']
464 cmd = get_ipython_cmd() + [subcommand, '--help-all']
464 out, err, rc = get_output_error_code(cmd)
465 out, err, rc = get_output_error_code(cmd)
465 nt.assert_equal(rc, 0, err)
466 nt.assert_equal(rc, 0, err)
466 nt.assert_not_in("Traceback", err)
467 nt.assert_not_in("Traceback", err)
467 nt.assert_in("Options", out)
468 nt.assert_in("Options", out)
468 nt.assert_in("Class", out)
469 nt.assert_in("Class", out)
469 return out, err
470 return out, err
470
471
@@ -1,145 +1,145 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for platutils.py
3 Tests for platutils.py
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import sys
17 import sys
18 import os
18 import os
19 from unittest import TestCase
19 from unittest import TestCase
20
20
21 import nose.tools as nt
21 import nose.tools as nt
22
22
23 from IPython.utils.process import (find_cmd, FindCmdError, arg_split,
23 from IPython.utils.process import (find_cmd, FindCmdError, arg_split,
24 system, getoutput, getoutputerror,
24 system, getoutput, getoutputerror,
25 get_output_error_code)
25 get_output_error_code)
26 from IPython.testing import decorators as dec
26 from IPython.testing import decorators as dec
27 from IPython.testing import tools as tt
27 from IPython.testing import tools as tt
28
28
29 python = os.path.basename(sys.executable)
29 python = os.path.basename(sys.executable)
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Tests
32 # Tests
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35
35
36 @dec.skip_win32
36 @dec.skip_win32
37 def test_find_cmd_ls():
37 def test_find_cmd_ls():
38 """Make sure we can find the full path to ls."""
38 """Make sure we can find the full path to ls."""
39 path = find_cmd('ls')
39 path = find_cmd('ls')
40 nt.assert_true(path.endswith('ls'))
40 nt.assert_true(path.endswith('ls'))
41
41
42
42
43 def has_pywin32():
43 def has_pywin32():
44 try:
44 try:
45 import win32api
45 import win32api
46 except ImportError:
46 except ImportError:
47 return False
47 return False
48 return True
48 return True
49
49
50
50
51 @dec.onlyif(has_pywin32, "This test requires win32api to run")
51 @dec.onlyif(has_pywin32, "This test requires win32api to run")
52 def test_find_cmd_pythonw():
52 def test_find_cmd_pythonw():
53 """Try to find pythonw on Windows."""
53 """Try to find pythonw on Windows."""
54 path = find_cmd('pythonw')
54 path = find_cmd('pythonw')
55 assert path.lower().endswith('pythonw.exe'), path
55 assert path.lower().endswith('pythonw.exe'), path
56
56
57
57
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
59 "This test runs on posix or in win32 with win32api installed")
59 "This test runs on posix or in win32 with win32api installed")
60 def test_find_cmd_fail():
60 def test_find_cmd_fail():
61 """Make sure that FindCmdError is raised if we can't find the cmd."""
61 """Make sure that FindCmdError is raised if we can't find the cmd."""
62 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
62 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
63
63
64
64
65 @dec.skip_win32
65 @dec.skip_win32
66 def test_arg_split():
66 def test_arg_split():
67 """Ensure that argument lines are correctly split like in a shell."""
67 """Ensure that argument lines are correctly split like in a shell."""
68 tests = [['hi', ['hi']],
68 tests = [['hi', ['hi']],
69 [u'hi', [u'hi']],
69 [u'hi', [u'hi']],
70 ['hello there', ['hello', 'there']],
70 ['hello there', ['hello', 'there']],
71 # \u01ce == \N{LATIN SMALL LETTER A WITH CARON}
71 # \u01ce == \N{LATIN SMALL LETTER A WITH CARON}
72 # Do not use \N because the tests crash with syntax error in
72 # Do not use \N because the tests crash with syntax error in
73 # some cases, for example windows python2.6.
73 # some cases, for example windows python2.6.
74 [u'h\u01cello', [u'h\u01cello']],
74 [u'h\u01cello', [u'h\u01cello']],
75 ['something "with quotes"', ['something', '"with quotes"']],
75 ['something "with quotes"', ['something', '"with quotes"']],
76 ]
76 ]
77 for argstr, argv in tests:
77 for argstr, argv in tests:
78 nt.assert_equal(arg_split(argstr), argv)
78 nt.assert_equal(arg_split(argstr), argv)
79
79
80 @dec.skip_if_not_win32
80 @dec.skip_if_not_win32
81 def test_arg_split_win32():
81 def test_arg_split_win32():
82 """Ensure that argument lines are correctly split like in a shell."""
82 """Ensure that argument lines are correctly split like in a shell."""
83 tests = [['hi', ['hi']],
83 tests = [['hi', ['hi']],
84 [u'hi', [u'hi']],
84 [u'hi', [u'hi']],
85 ['hello there', ['hello', 'there']],
85 ['hello there', ['hello', 'there']],
86 [u'h\u01cello', [u'h\u01cello']],
86 [u'h\u01cello', [u'h\u01cello']],
87 ['something "with quotes"', ['something', 'with quotes']],
87 ['something "with quotes"', ['something', 'with quotes']],
88 ]
88 ]
89 for argstr, argv in tests:
89 for argstr, argv in tests:
90 nt.assert_equal(arg_split(argstr), argv)
90 nt.assert_equal(arg_split(argstr), argv)
91
91
92
92
93 class SubProcessTestCase(TestCase, tt.TempFileMixin):
93 class SubProcessTestCase(tt.TempFileMixin):
94 def setUp(self):
94 def setUp(self):
95 """Make a valid python temp file."""
95 """Make a valid python temp file."""
96 lines = [ "import sys",
96 lines = [ "import sys",
97 "print('on stdout', end='', file=sys.stdout)",
97 "print('on stdout', end='', file=sys.stdout)",
98 "print('on stderr', end='', file=sys.stderr)",
98 "print('on stderr', end='', file=sys.stderr)",
99 "sys.stdout.flush()",
99 "sys.stdout.flush()",
100 "sys.stderr.flush()"]
100 "sys.stderr.flush()"]
101 self.mktmp('\n'.join(lines))
101 self.mktmp('\n'.join(lines))
102
102
103 def test_system(self):
103 def test_system(self):
104 status = system('%s "%s"' % (python, self.fname))
104 status = system('%s "%s"' % (python, self.fname))
105 self.assertEqual(status, 0)
105 self.assertEqual(status, 0)
106
106
107 def test_system_quotes(self):
107 def test_system_quotes(self):
108 status = system('%s -c "import sys"' % python)
108 status = system('%s -c "import sys"' % python)
109 self.assertEqual(status, 0)
109 self.assertEqual(status, 0)
110
110
111 def test_getoutput(self):
111 def test_getoutput(self):
112 out = getoutput('%s "%s"' % (python, self.fname))
112 out = getoutput('%s "%s"' % (python, self.fname))
113 # we can't rely on the order the line buffered streams are flushed
113 # we can't rely on the order the line buffered streams are flushed
114 try:
114 try:
115 self.assertEqual(out, 'on stderron stdout')
115 self.assertEqual(out, 'on stderron stdout')
116 except AssertionError:
116 except AssertionError:
117 self.assertEqual(out, 'on stdouton stderr')
117 self.assertEqual(out, 'on stdouton stderr')
118
118
119 def test_getoutput_quoted(self):
119 def test_getoutput_quoted(self):
120 out = getoutput('%s -c "print (1)"' % python)
120 out = getoutput('%s -c "print (1)"' % python)
121 self.assertEqual(out.strip(), '1')
121 self.assertEqual(out.strip(), '1')
122
122
123 #Invalid quoting on windows
123 #Invalid quoting on windows
124 @dec.skip_win32
124 @dec.skip_win32
125 def test_getoutput_quoted2(self):
125 def test_getoutput_quoted2(self):
126 out = getoutput("%s -c 'print (1)'" % python)
126 out = getoutput("%s -c 'print (1)'" % python)
127 self.assertEqual(out.strip(), '1')
127 self.assertEqual(out.strip(), '1')
128 out = getoutput("%s -c 'print (\"1\")'" % python)
128 out = getoutput("%s -c 'print (\"1\")'" % python)
129 self.assertEqual(out.strip(), '1')
129 self.assertEqual(out.strip(), '1')
130
130
131 def test_getoutput_error(self):
131 def test_getoutput_error(self):
132 out, err = getoutputerror('%s "%s"' % (python, self.fname))
132 out, err = getoutputerror('%s "%s"' % (python, self.fname))
133 self.assertEqual(out, 'on stdout')
133 self.assertEqual(out, 'on stdout')
134 self.assertEqual(err, 'on stderr')
134 self.assertEqual(err, 'on stderr')
135
135
136 def test_get_output_error_code(self):
136 def test_get_output_error_code(self):
137 quiet_exit = '%s -c "import sys; sys.exit(1)"' % python
137 quiet_exit = '%s -c "import sys; sys.exit(1)"' % python
138 out, err, code = get_output_error_code(quiet_exit)
138 out, err, code = get_output_error_code(quiet_exit)
139 self.assertEqual(out, '')
139 self.assertEqual(out, '')
140 self.assertEqual(err, '')
140 self.assertEqual(err, '')
141 self.assertEqual(code, 1)
141 self.assertEqual(code, 1)
142 out, err, code = get_output_error_code('%s "%s"' % (python, self.fname))
142 out, err, code = get_output_error_code('%s "%s"' % (python, self.fname))
143 self.assertEqual(out, 'on stdout')
143 self.assertEqual(out, 'on stdout')
144 self.assertEqual(err, 'on stderr')
144 self.assertEqual(err, 'on stderr')
145 self.assertEqual(code, 0)
145 self.assertEqual(code, 0)
General Comments 0
You need to be logged in to leave comments. Login now