##// END OF EJS Templates
Make tests for '%run -m' more robust
Thomas Kluyver -
Show More
@@ -1,432 +1,433 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 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import functools
16 import functools
17 import os
17 import os
18 import random
18 import random
19 import sys
19 import sys
20 import tempfile
20 import tempfile
21 import textwrap
21 import textwrap
22 import unittest
22 import unittest
23
23
24 import nose.tools as nt
24 import nose.tools as nt
25 from nose import SkipTest
25 from nose import SkipTest
26
26
27 from IPython.testing import decorators as dec
27 from IPython.testing import decorators as dec
28 from IPython.testing import tools as tt
28 from IPython.testing import tools as tt
29 from IPython.utils import py3compat
29 from IPython.utils import py3compat
30 from IPython.utils.tempdir import TemporaryDirectory
30 from IPython.utils.tempdir import TemporaryDirectory
31 from IPython.core import debugger
31 from IPython.core import debugger
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Test functions begin
34 # Test functions begin
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 def doctest_refbug():
37 def doctest_refbug():
38 """Very nasty problem with references held by multiple runs of a script.
38 """Very nasty problem with references held by multiple runs of a script.
39 See: https://github.com/ipython/ipython/issues/141
39 See: https://github.com/ipython/ipython/issues/141
40
40
41 In [1]: _ip.clear_main_mod_cache()
41 In [1]: _ip.clear_main_mod_cache()
42 # random
42 # random
43
43
44 In [2]: %run refbug
44 In [2]: %run refbug
45
45
46 In [3]: call_f()
46 In [3]: call_f()
47 lowercased: hello
47 lowercased: hello
48
48
49 In [4]: %run refbug
49 In [4]: %run refbug
50
50
51 In [5]: call_f()
51 In [5]: call_f()
52 lowercased: hello
52 lowercased: hello
53 lowercased: hello
53 lowercased: hello
54 """
54 """
55
55
56
56
57 def doctest_run_builtins():
57 def doctest_run_builtins():
58 r"""Check that %run doesn't damage __builtins__.
58 r"""Check that %run doesn't damage __builtins__.
59
59
60 In [1]: import tempfile
60 In [1]: import tempfile
61
61
62 In [2]: bid1 = id(__builtins__)
62 In [2]: bid1 = id(__builtins__)
63
63
64 In [3]: fname = tempfile.mkstemp('.py')[1]
64 In [3]: fname = tempfile.mkstemp('.py')[1]
65
65
66 In [3]: f = open(fname,'w')
66 In [3]: f = open(fname,'w')
67
67
68 In [4]: dummy= f.write('pass\n')
68 In [4]: dummy= f.write('pass\n')
69
69
70 In [5]: f.flush()
70 In [5]: f.flush()
71
71
72 In [6]: t1 = type(__builtins__)
72 In [6]: t1 = type(__builtins__)
73
73
74 In [7]: %run $fname
74 In [7]: %run $fname
75
75
76 In [7]: f.close()
76 In [7]: f.close()
77
77
78 In [8]: bid2 = id(__builtins__)
78 In [8]: bid2 = id(__builtins__)
79
79
80 In [9]: t2 = type(__builtins__)
80 In [9]: t2 = type(__builtins__)
81
81
82 In [10]: t1 == t2
82 In [10]: t1 == t2
83 Out[10]: True
83 Out[10]: True
84
84
85 In [10]: bid1 == bid2
85 In [10]: bid1 == bid2
86 Out[10]: True
86 Out[10]: True
87
87
88 In [12]: try:
88 In [12]: try:
89 ....: os.unlink(fname)
89 ....: os.unlink(fname)
90 ....: except:
90 ....: except:
91 ....: pass
91 ....: pass
92 ....:
92 ....:
93 """
93 """
94
94
95
95
96 def doctest_run_option_parser():
96 def doctest_run_option_parser():
97 r"""Test option parser in %run.
97 r"""Test option parser in %run.
98
98
99 In [1]: %run print_argv.py
99 In [1]: %run print_argv.py
100 []
100 []
101
101
102 In [2]: %run print_argv.py print*.py
102 In [2]: %run print_argv.py print*.py
103 ['print_argv.py']
103 ['print_argv.py']
104
104
105 In [3]: %run -G print_argv.py print*.py
105 In [3]: %run -G print_argv.py print*.py
106 ['print*.py']
106 ['print*.py']
107
107
108 """
108 """
109
109
110
110
111 @dec.skip_win32
111 @dec.skip_win32
112 def doctest_run_option_parser_for_posix():
112 def doctest_run_option_parser_for_posix():
113 r"""Test option parser in %run (Linux/OSX specific).
113 r"""Test option parser in %run (Linux/OSX specific).
114
114
115 You need double quote to escape glob in POSIX systems:
115 You need double quote to escape glob in POSIX systems:
116
116
117 In [1]: %run print_argv.py print\\*.py
117 In [1]: %run print_argv.py print\\*.py
118 ['print*.py']
118 ['print*.py']
119
119
120 You can't use quote to escape glob in POSIX systems:
120 You can't use quote to escape glob in POSIX systems:
121
121
122 In [2]: %run print_argv.py 'print*.py'
122 In [2]: %run print_argv.py 'print*.py'
123 ['print_argv.py']
123 ['print_argv.py']
124
124
125 """
125 """
126
126
127
127
128 @dec.skip_if_not_win32
128 @dec.skip_if_not_win32
129 def doctest_run_option_parser_for_windows():
129 def doctest_run_option_parser_for_windows():
130 r"""Test option parser in %run (Windows specific).
130 r"""Test option parser in %run (Windows specific).
131
131
132 In Windows, you can't escape ``*` `by backslash:
132 In Windows, you can't escape ``*` `by backslash:
133
133
134 In [1]: %run print_argv.py print\\*.py
134 In [1]: %run print_argv.py print\\*.py
135 ['print\\*.py']
135 ['print\\*.py']
136
136
137 You can use quote to escape glob:
137 You can use quote to escape glob:
138
138
139 In [2]: %run print_argv.py 'print*.py'
139 In [2]: %run print_argv.py 'print*.py'
140 ['print*.py']
140 ['print*.py']
141
141
142 """
142 """
143
143
144
144
145 @py3compat.doctest_refactor_print
145 @py3compat.doctest_refactor_print
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 """Make a valid python temp file."""
169 """Make a valid python temp file."""
170 self.mktmp('pass\n')
170 self.mktmp('pass\n')
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_prompts(self):
204 def test_prompts(self):
205 """Test that prompts correctly generate after %run"""
205 """Test that prompts correctly generate after %run"""
206 self.run_tmpfile()
206 self.run_tmpfile()
207 _ip = get_ipython()
207 _ip = get_ipython()
208 p2 = _ip.prompt_manager.render('in2').strip()
208 p2 = _ip.prompt_manager.render('in2').strip()
209 nt.assert_equal(p2[:3], '...')
209 nt.assert_equal(p2[:3], '...')
210
210
211 def test_run_profile( self ):
211 def test_run_profile( self ):
212 """Test that the option -p, which invokes the profiler, do not
212 """Test that the option -p, which invokes the profiler, do not
213 crash by invoking execfile"""
213 crash by invoking execfile"""
214 _ip = get_ipython()
214 _ip = get_ipython()
215 self.run_tmpfile_p()
215 self.run_tmpfile_p()
216
216
217
217
218 class TestMagicRunSimple(tt.TempFileMixin):
218 class TestMagicRunSimple(tt.TempFileMixin):
219
219
220 def test_simpledef(self):
220 def test_simpledef(self):
221 """Test that simple class definitions work."""
221 """Test that simple class definitions work."""
222 src = ("class foo: pass\n"
222 src = ("class foo: pass\n"
223 "def f(): return foo()")
223 "def f(): return foo()")
224 self.mktmp(src)
224 self.mktmp(src)
225 _ip.magic('run %s' % self.fname)
225 _ip.magic('run %s' % self.fname)
226 _ip.run_cell('t = isinstance(f(), foo)')
226 _ip.run_cell('t = isinstance(f(), foo)')
227 nt.assert_true(_ip.user_ns['t'])
227 nt.assert_true(_ip.user_ns['t'])
228
228
229 def test_obj_del(self):
229 def test_obj_del(self):
230 """Test that object's __del__ methods are called on exit."""
230 """Test that object's __del__ methods are called on exit."""
231 if sys.platform == 'win32':
231 if sys.platform == 'win32':
232 try:
232 try:
233 import win32api
233 import win32api
234 except ImportError:
234 except ImportError:
235 raise SkipTest("Test requires pywin32")
235 raise SkipTest("Test requires pywin32")
236 src = ("class A(object):\n"
236 src = ("class A(object):\n"
237 " def __del__(self):\n"
237 " def __del__(self):\n"
238 " print 'object A deleted'\n"
238 " print 'object A deleted'\n"
239 "a = A()\n")
239 "a = A()\n")
240 self.mktmp(py3compat.doctest_refactor_print(src))
240 self.mktmp(py3compat.doctest_refactor_print(src))
241 if dec.module_not_available('sqlite3'):
241 if dec.module_not_available('sqlite3'):
242 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
242 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
243 else:
243 else:
244 err = None
244 err = None
245 tt.ipexec_validate(self.fname, 'object A deleted', err)
245 tt.ipexec_validate(self.fname, 'object A deleted', err)
246
246
247 def test_aggressive_namespace_cleanup(self):
247 def test_aggressive_namespace_cleanup(self):
248 """Test that namespace cleanup is not too aggressive GH-238
248 """Test that namespace cleanup is not too aggressive GH-238
249
249
250 Returning from another run magic deletes the namespace"""
250 Returning from another run magic deletes the namespace"""
251 # see ticket https://github.com/ipython/ipython/issues/238
251 # see ticket https://github.com/ipython/ipython/issues/238
252 class secondtmp(tt.TempFileMixin): pass
252 class secondtmp(tt.TempFileMixin): pass
253 empty = secondtmp()
253 empty = secondtmp()
254 empty.mktmp('')
254 empty.mktmp('')
255 # On Windows, the filename will have \users in it, so we need to use the
255 # On Windows, the filename will have \users in it, so we need to use the
256 # repr so that the \u becomes \\u.
256 # repr so that the \u becomes \\u.
257 src = ("ip = get_ipython()\n"
257 src = ("ip = get_ipython()\n"
258 "for i in range(5):\n"
258 "for i in range(5):\n"
259 " try:\n"
259 " try:\n"
260 " ip.magic(%r)\n"
260 " ip.magic(%r)\n"
261 " except NameError as e:\n"
261 " except NameError as e:\n"
262 " print(i)\n"
262 " print(i)\n"
263 " break\n" % ('run ' + empty.fname))
263 " break\n" % ('run ' + empty.fname))
264 self.mktmp(src)
264 self.mktmp(src)
265 _ip.magic('run %s' % self.fname)
265 _ip.magic('run %s' % self.fname)
266 _ip.run_cell('ip == get_ipython()')
266 _ip.run_cell('ip == get_ipython()')
267 nt.assert_equal(_ip.user_ns['i'], 4)
267 nt.assert_equal(_ip.user_ns['i'], 4)
268
268
269 def test_run_second(self):
269 def test_run_second(self):
270 """Test that running a second file doesn't clobber the first, gh-3547
270 """Test that running a second file doesn't clobber the first, gh-3547
271 """
271 """
272 self.mktmp("avar = 1\n"
272 self.mktmp("avar = 1\n"
273 "def afunc():\n"
273 "def afunc():\n"
274 " return avar\n")
274 " return avar\n")
275
275
276 empty = tt.TempFileMixin()
276 empty = tt.TempFileMixin()
277 empty.mktmp("")
277 empty.mktmp("")
278
278
279 _ip.magic('run %s' % self.fname)
279 _ip.magic('run %s' % self.fname)
280 _ip.magic('run %s' % empty.fname)
280 _ip.magic('run %s' % empty.fname)
281 nt.assert_equal(_ip.user_ns['afunc'](), 1)
281 nt.assert_equal(_ip.user_ns['afunc'](), 1)
282
282
283 @dec.skip_win32
283 @dec.skip_win32
284 def test_tclass(self):
284 def test_tclass(self):
285 mydir = os.path.dirname(__file__)
285 mydir = os.path.dirname(__file__)
286 tc = os.path.join(mydir, 'tclass')
286 tc = os.path.join(mydir, 'tclass')
287 src = ("%%run '%s' C-first\n"
287 src = ("%%run '%s' C-first\n"
288 "%%run '%s' C-second\n"
288 "%%run '%s' C-second\n"
289 "%%run '%s' C-third\n") % (tc, tc, tc)
289 "%%run '%s' C-third\n") % (tc, tc, tc)
290 self.mktmp(src, '.ipy')
290 self.mktmp(src, '.ipy')
291 out = """\
291 out = """\
292 ARGV 1-: ['C-first']
292 ARGV 1-: ['C-first']
293 ARGV 1-: ['C-second']
293 ARGV 1-: ['C-second']
294 tclass.py: deleting object: C-first
294 tclass.py: deleting object: C-first
295 ARGV 1-: ['C-third']
295 ARGV 1-: ['C-third']
296 tclass.py: deleting object: C-second
296 tclass.py: deleting object: C-second
297 tclass.py: deleting object: C-third
297 tclass.py: deleting object: C-third
298 """
298 """
299 if dec.module_not_available('sqlite3'):
299 if dec.module_not_available('sqlite3'):
300 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
300 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
301 else:
301 else:
302 err = None
302 err = None
303 tt.ipexec_validate(self.fname, out, err)
303 tt.ipexec_validate(self.fname, out, err)
304
304
305 def test_run_i_after_reset(self):
305 def test_run_i_after_reset(self):
306 """Check that %run -i still works after %reset (gh-693)"""
306 """Check that %run -i still works after %reset (gh-693)"""
307 src = "yy = zz\n"
307 src = "yy = zz\n"
308 self.mktmp(src)
308 self.mktmp(src)
309 _ip.run_cell("zz = 23")
309 _ip.run_cell("zz = 23")
310 _ip.magic('run -i %s' % self.fname)
310 _ip.magic('run -i %s' % self.fname)
311 nt.assert_equal(_ip.user_ns['yy'], 23)
311 nt.assert_equal(_ip.user_ns['yy'], 23)
312 _ip.magic('reset -f')
312 _ip.magic('reset -f')
313 _ip.run_cell("zz = 23")
313 _ip.run_cell("zz = 23")
314 _ip.magic('run -i %s' % self.fname)
314 _ip.magic('run -i %s' % self.fname)
315 nt.assert_equal(_ip.user_ns['yy'], 23)
315 nt.assert_equal(_ip.user_ns['yy'], 23)
316
316
317 def test_unicode(self):
317 def test_unicode(self):
318 """Check that files in odd encodings are accepted."""
318 """Check that files in odd encodings are accepted."""
319 mydir = os.path.dirname(__file__)
319 mydir = os.path.dirname(__file__)
320 na = os.path.join(mydir, 'nonascii.py')
320 na = os.path.join(mydir, 'nonascii.py')
321 _ip.magic('run "%s"' % na)
321 _ip.magic('run "%s"' % na)
322 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
322 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
323
323
324 def test_run_py_file_attribute(self):
324 def test_run_py_file_attribute(self):
325 """Test handling of `__file__` attribute in `%run <file>.py`."""
325 """Test handling of `__file__` attribute in `%run <file>.py`."""
326 src = "t = __file__\n"
326 src = "t = __file__\n"
327 self.mktmp(src)
327 self.mktmp(src)
328 _missing = object()
328 _missing = object()
329 file1 = _ip.user_ns.get('__file__', _missing)
329 file1 = _ip.user_ns.get('__file__', _missing)
330 _ip.magic('run %s' % self.fname)
330 _ip.magic('run %s' % self.fname)
331 file2 = _ip.user_ns.get('__file__', _missing)
331 file2 = _ip.user_ns.get('__file__', _missing)
332
332
333 # Check that __file__ was equal to the filename in the script's
333 # Check that __file__ was equal to the filename in the script's
334 # namespace.
334 # namespace.
335 nt.assert_equal(_ip.user_ns['t'], self.fname)
335 nt.assert_equal(_ip.user_ns['t'], self.fname)
336
336
337 # Check that __file__ was not leaked back into user_ns.
337 # Check that __file__ was not leaked back into user_ns.
338 nt.assert_equal(file1, file2)
338 nt.assert_equal(file1, file2)
339
339
340 def test_run_ipy_file_attribute(self):
340 def test_run_ipy_file_attribute(self):
341 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
341 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
342 src = "t = __file__\n"
342 src = "t = __file__\n"
343 self.mktmp(src, ext='.ipy')
343 self.mktmp(src, ext='.ipy')
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_formatting(self):
356 def test_run_formatting(self):
357 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
357 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
358 src = "pass"
358 src = "pass"
359 self.mktmp(src)
359 self.mktmp(src)
360 _ip.magic('run -t -N 1 %s' % self.fname)
360 _ip.magic('run -t -N 1 %s' % self.fname)
361 _ip.magic('run -t -N 10 %s' % self.fname)
361 _ip.magic('run -t -N 10 %s' % self.fname)
362
362
363
363
364 class TestMagicRunWithPackage(unittest.TestCase):
364 class TestMagicRunWithPackage(unittest.TestCase):
365
365
366 def writefile(self, name, content):
366 def writefile(self, name, content):
367 path = os.path.join(self.tempdir.name, name)
367 path = os.path.join(self.tempdir.name, name)
368 d = os.path.dirname(path)
368 d = os.path.dirname(path)
369 if not os.path.isdir(d):
369 if not os.path.isdir(d):
370 os.makedirs(d)
370 os.makedirs(d)
371 with open(path, 'w') as f:
371 with open(path, 'w') as f:
372 f.write(textwrap.dedent(content))
372 f.write(textwrap.dedent(content))
373
373
374 def setUp(self):
374 def setUp(self):
375 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
375 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
376 """Temporary valid python package name."""
376 """Temporary valid python package name."""
377
377
378 self.value = int(random.random() * 10000)
378 self.value = int(random.random() * 10000)
379
379
380 self.tempdir = TemporaryDirectory()
380 self.tempdir = TemporaryDirectory()
381 self.__orig_cwd = os.getcwdu()
381 self.__orig_cwd = os.getcwdu()
382 sys.path.insert(0, self.tempdir.name)
382 sys.path.insert(0, self.tempdir.name)
383
383
384 self.writefile(os.path.join(package, '__init__.py'), '')
384 self.writefile(os.path.join(package, '__init__.py'), '')
385 self.writefile(os.path.join(package, 'sub.py'), """
385 self.writefile(os.path.join(package, 'sub.py'), """
386 x = {0!r}
386 x = {0!r}
387 """.format(self.value))
387 """.format(self.value))
388 self.writefile(os.path.join(package, 'relative.py'), """
388 self.writefile(os.path.join(package, 'relative.py'), """
389 from .sub import x
389 from .sub import x
390 """)
390 """)
391 self.writefile(os.path.join(package, 'absolute.py'), """
391 self.writefile(os.path.join(package, 'absolute.py'), """
392 from {0}.sub import x
392 from {0}.sub import x
393 """.format(package))
393 """.format(package))
394
394
395 def tearDown(self):
395 def tearDown(self):
396 os.chdir(self.__orig_cwd)
396 os.chdir(self.__orig_cwd)
397 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
397 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
398 self.tempdir.cleanup()
398 self.tempdir.cleanup()
399
399
400 def check_run_submodule(self, submodule, opts=''):
400 def check_run_submodule(self, submodule, opts=''):
401 _ip.user_ns.pop('x', None)
401 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
402 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
402 self.assertEqual(_ip.user_ns['x'], self.value,
403 self.assertEqual(_ip.user_ns['x'], self.value,
403 'Variable `x` is not loaded from module `{0}`.'
404 'Variable `x` is not loaded from module `{0}`.'
404 .format(submodule))
405 .format(submodule))
405
406
406 def test_run_submodule_with_absolute_import(self):
407 def test_run_submodule_with_absolute_import(self):
407 self.check_run_submodule('absolute')
408 self.check_run_submodule('absolute')
408
409
409 def test_run_submodule_with_relative_import(self):
410 def test_run_submodule_with_relative_import(self):
410 """Run submodule that has a relative import statement (#2727)."""
411 """Run submodule that has a relative import statement (#2727)."""
411 self.check_run_submodule('relative')
412 self.check_run_submodule('relative')
412
413
413 def test_prun_submodule_with_absolute_import(self):
414 def test_prun_submodule_with_absolute_import(self):
414 self.check_run_submodule('absolute', '-p')
415 self.check_run_submodule('absolute', '-p')
415
416
416 def test_prun_submodule_with_relative_import(self):
417 def test_prun_submodule_with_relative_import(self):
417 self.check_run_submodule('relative', '-p')
418 self.check_run_submodule('relative', '-p')
418
419
419 def with_fake_debugger(func):
420 def with_fake_debugger(func):
420 @functools.wraps(func)
421 @functools.wraps(func)
421 def wrapper(*args, **kwds):
422 def wrapper(*args, **kwds):
422 with tt.monkeypatch(debugger.Pdb, 'run', staticmethod(eval)):
423 with tt.monkeypatch(debugger.Pdb, 'run', staticmethod(eval)):
423 return func(*args, **kwds)
424 return func(*args, **kwds)
424 return wrapper
425 return wrapper
425
426
426 @with_fake_debugger
427 @with_fake_debugger
427 def test_debug_run_submodule_with_absolute_import(self):
428 def test_debug_run_submodule_with_absolute_import(self):
428 self.check_run_submodule('absolute', '-d')
429 self.check_run_submodule('absolute', '-d')
429
430
430 @with_fake_debugger
431 @with_fake_debugger
431 def test_debug_run_submodule_with_relative_import(self):
432 def test_debug_run_submodule_with_relative_import(self):
432 self.check_run_submodule('relative', '-d')
433 self.check_run_submodule('relative', '-d')
General Comments 0
You need to be logged in to leave comments. Login now