##// END OF EJS Templates
Test for #2727 (%run -m doesn't support relative imports)...
Takafumi Arakaki -
Show More
@@ -1,339 +1,393 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 os
16 import os
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import unittest
20 import textwrap
21 import random
19
22
20 import nose.tools as nt
23 import nose.tools as nt
21 from nose import SkipTest
24 from nose import SkipTest
22
25
23 from IPython.testing import decorators as dec
26 from IPython.testing import decorators as dec
24 from IPython.testing import tools as tt
27 from IPython.testing import tools as tt
25 from IPython.utils import py3compat
28 from IPython.utils import py3compat
29 from IPython.utils.tempdir import TemporaryDirectory
26
30
27 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
28 # Test functions begin
32 # Test functions begin
29 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
30
34
31 def doctest_refbug():
35 def doctest_refbug():
32 """Very nasty problem with references held by multiple runs of a script.
36 """Very nasty problem with references held by multiple runs of a script.
33 See: https://github.com/ipython/ipython/issues/141
37 See: https://github.com/ipython/ipython/issues/141
34
38
35 In [1]: _ip.clear_main_mod_cache()
39 In [1]: _ip.clear_main_mod_cache()
36 # random
40 # random
37
41
38 In [2]: %run refbug
42 In [2]: %run refbug
39
43
40 In [3]: call_f()
44 In [3]: call_f()
41 lowercased: hello
45 lowercased: hello
42
46
43 In [4]: %run refbug
47 In [4]: %run refbug
44
48
45 In [5]: call_f()
49 In [5]: call_f()
46 lowercased: hello
50 lowercased: hello
47 lowercased: hello
51 lowercased: hello
48 """
52 """
49
53
50
54
51 def doctest_run_builtins():
55 def doctest_run_builtins():
52 r"""Check that %run doesn't damage __builtins__.
56 r"""Check that %run doesn't damage __builtins__.
53
57
54 In [1]: import tempfile
58 In [1]: import tempfile
55
59
56 In [2]: bid1 = id(__builtins__)
60 In [2]: bid1 = id(__builtins__)
57
61
58 In [3]: fname = tempfile.mkstemp('.py')[1]
62 In [3]: fname = tempfile.mkstemp('.py')[1]
59
63
60 In [3]: f = open(fname,'w')
64 In [3]: f = open(fname,'w')
61
65
62 In [4]: dummy= f.write('pass\n')
66 In [4]: dummy= f.write('pass\n')
63
67
64 In [5]: f.flush()
68 In [5]: f.flush()
65
69
66 In [6]: t1 = type(__builtins__)
70 In [6]: t1 = type(__builtins__)
67
71
68 In [7]: %run $fname
72 In [7]: %run $fname
69
73
70 In [7]: f.close()
74 In [7]: f.close()
71
75
72 In [8]: bid2 = id(__builtins__)
76 In [8]: bid2 = id(__builtins__)
73
77
74 In [9]: t2 = type(__builtins__)
78 In [9]: t2 = type(__builtins__)
75
79
76 In [10]: t1 == t2
80 In [10]: t1 == t2
77 Out[10]: True
81 Out[10]: True
78
82
79 In [10]: bid1 == bid2
83 In [10]: bid1 == bid2
80 Out[10]: True
84 Out[10]: True
81
85
82 In [12]: try:
86 In [12]: try:
83 ....: os.unlink(fname)
87 ....: os.unlink(fname)
84 ....: except:
88 ....: except:
85 ....: pass
89 ....: pass
86 ....:
90 ....:
87 """
91 """
88
92
89
93
90 def doctest_run_option_parser():
94 def doctest_run_option_parser():
91 r"""Test option parser in %run.
95 r"""Test option parser in %run.
92
96
93 In [1]: %run print_argv.py
97 In [1]: %run print_argv.py
94 []
98 []
95
99
96 In [2]: %run print_argv.py print*.py
100 In [2]: %run print_argv.py print*.py
97 ['print_argv.py']
101 ['print_argv.py']
98
102
99 In [3]: %run -G print_argv.py print*.py
103 In [3]: %run -G print_argv.py print*.py
100 ['print*.py']
104 ['print*.py']
101
105
102 """
106 """
103
107
104
108
105 @dec.skip_win32
109 @dec.skip_win32
106 def doctest_run_option_parser_for_posix():
110 def doctest_run_option_parser_for_posix():
107 r"""Test option parser in %run (Linux/OSX specific).
111 r"""Test option parser in %run (Linux/OSX specific).
108
112
109 You need double quote to escape glob in POSIX systems:
113 You need double quote to escape glob in POSIX systems:
110
114
111 In [1]: %run print_argv.py print\\*.py
115 In [1]: %run print_argv.py print\\*.py
112 ['print*.py']
116 ['print*.py']
113
117
114 You can't use quote to escape glob in POSIX systems:
118 You can't use quote to escape glob in POSIX systems:
115
119
116 In [2]: %run print_argv.py 'print*.py'
120 In [2]: %run print_argv.py 'print*.py'
117 ['print_argv.py']
121 ['print_argv.py']
118
122
119 """
123 """
120
124
121
125
122 @dec.skip_if_not_win32
126 @dec.skip_if_not_win32
123 def doctest_run_option_parser_for_windows():
127 def doctest_run_option_parser_for_windows():
124 r"""Test option parser in %run (Windows specific).
128 r"""Test option parser in %run (Windows specific).
125
129
126 In Windows, you can't escape ``*` `by backslash:
130 In Windows, you can't escape ``*` `by backslash:
127
131
128 In [1]: %run print_argv.py print\\*.py
132 In [1]: %run print_argv.py print\\*.py
129 ['print\\*.py']
133 ['print\\*.py']
130
134
131 You can use quote to escape glob:
135 You can use quote to escape glob:
132
136
133 In [2]: %run print_argv.py 'print*.py'
137 In [2]: %run print_argv.py 'print*.py'
134 ['print*.py']
138 ['print*.py']
135
139
136 """
140 """
137
141
138
142
139 @py3compat.doctest_refactor_print
143 @py3compat.doctest_refactor_print
140 def doctest_reset_del():
144 def doctest_reset_del():
141 """Test that resetting doesn't cause errors in __del__ methods.
145 """Test that resetting doesn't cause errors in __del__ methods.
142
146
143 In [2]: class A(object):
147 In [2]: class A(object):
144 ...: def __del__(self):
148 ...: def __del__(self):
145 ...: print str("Hi")
149 ...: print str("Hi")
146 ...:
150 ...:
147
151
148 In [3]: a = A()
152 In [3]: a = A()
149
153
150 In [4]: get_ipython().reset()
154 In [4]: get_ipython().reset()
151 Hi
155 Hi
152
156
153 In [5]: 1+1
157 In [5]: 1+1
154 Out[5]: 2
158 Out[5]: 2
155 """
159 """
156
160
157 # For some tests, it will be handy to organize them in a class with a common
161 # For some tests, it will be handy to organize them in a class with a common
158 # setup that makes a temp file
162 # setup that makes a temp file
159
163
160 class TestMagicRunPass(tt.TempFileMixin):
164 class TestMagicRunPass(tt.TempFileMixin):
161
165
162 def setup(self):
166 def setup(self):
163 """Make a valid python temp file."""
167 """Make a valid python temp file."""
164 self.mktmp('pass\n')
168 self.mktmp('pass\n')
165
169
166 def run_tmpfile(self):
170 def run_tmpfile(self):
167 _ip = get_ipython()
171 _ip = get_ipython()
168 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
172 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
169 # See below and ticket https://bugs.launchpad.net/bugs/366353
173 # See below and ticket https://bugs.launchpad.net/bugs/366353
170 _ip.magic('run %s' % self.fname)
174 _ip.magic('run %s' % self.fname)
171
175
172 def run_tmpfile_p(self):
176 def run_tmpfile_p(self):
173 _ip = get_ipython()
177 _ip = get_ipython()
174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
178 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
175 # See below and ticket https://bugs.launchpad.net/bugs/366353
179 # See below and ticket https://bugs.launchpad.net/bugs/366353
176 _ip.magic('run -p %s' % self.fname)
180 _ip.magic('run -p %s' % self.fname)
177
181
178 def test_builtins_id(self):
182 def test_builtins_id(self):
179 """Check that %run doesn't damage __builtins__ """
183 """Check that %run doesn't damage __builtins__ """
180 _ip = get_ipython()
184 _ip = get_ipython()
181 # Test that the id of __builtins__ is not modified by %run
185 # Test that the id of __builtins__ is not modified by %run
182 bid1 = id(_ip.user_ns['__builtins__'])
186 bid1 = id(_ip.user_ns['__builtins__'])
183 self.run_tmpfile()
187 self.run_tmpfile()
184 bid2 = id(_ip.user_ns['__builtins__'])
188 bid2 = id(_ip.user_ns['__builtins__'])
185 nt.assert_equal(bid1, bid2)
189 nt.assert_equal(bid1, bid2)
186
190
187 def test_builtins_type(self):
191 def test_builtins_type(self):
188 """Check that the type of __builtins__ doesn't change with %run.
192 """Check that the type of __builtins__ doesn't change with %run.
189
193
190 However, the above could pass if __builtins__ was already modified to
194 However, the above could pass if __builtins__ was already modified to
191 be a dict (it should be a module) by a previous use of %run. So we
195 be a dict (it should be a module) by a previous use of %run. So we
192 also check explicitly that it really is a module:
196 also check explicitly that it really is a module:
193 """
197 """
194 _ip = get_ipython()
198 _ip = get_ipython()
195 self.run_tmpfile()
199 self.run_tmpfile()
196 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
200 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
197
201
198 def test_prompts(self):
202 def test_prompts(self):
199 """Test that prompts correctly generate after %run"""
203 """Test that prompts correctly generate after %run"""
200 self.run_tmpfile()
204 self.run_tmpfile()
201 _ip = get_ipython()
205 _ip = get_ipython()
202 p2 = _ip.prompt_manager.render('in2').strip()
206 p2 = _ip.prompt_manager.render('in2').strip()
203 nt.assert_equal(p2[:3], '...')
207 nt.assert_equal(p2[:3], '...')
204
208
205 def test_run_profile( self ):
209 def test_run_profile( self ):
206 """Test that the option -p, which invokes the profiler, do not
210 """Test that the option -p, which invokes the profiler, do not
207 crash by invoking execfile"""
211 crash by invoking execfile"""
208 _ip = get_ipython()
212 _ip = get_ipython()
209 self.run_tmpfile_p()
213 self.run_tmpfile_p()
210
214
211
215
212 class TestMagicRunSimple(tt.TempFileMixin):
216 class TestMagicRunSimple(tt.TempFileMixin):
213
217
214 def test_simpledef(self):
218 def test_simpledef(self):
215 """Test that simple class definitions work."""
219 """Test that simple class definitions work."""
216 src = ("class foo: pass\n"
220 src = ("class foo: pass\n"
217 "def f(): return foo()")
221 "def f(): return foo()")
218 self.mktmp(src)
222 self.mktmp(src)
219 _ip.magic('run %s' % self.fname)
223 _ip.magic('run %s' % self.fname)
220 _ip.run_cell('t = isinstance(f(), foo)')
224 _ip.run_cell('t = isinstance(f(), foo)')
221 nt.assert_true(_ip.user_ns['t'])
225 nt.assert_true(_ip.user_ns['t'])
222
226
223 def test_obj_del(self):
227 def test_obj_del(self):
224 """Test that object's __del__ methods are called on exit."""
228 """Test that object's __del__ methods are called on exit."""
225 if sys.platform == 'win32':
229 if sys.platform == 'win32':
226 try:
230 try:
227 import win32api
231 import win32api
228 except ImportError:
232 except ImportError:
229 raise SkipTest("Test requires pywin32")
233 raise SkipTest("Test requires pywin32")
230 src = ("class A(object):\n"
234 src = ("class A(object):\n"
231 " def __del__(self):\n"
235 " def __del__(self):\n"
232 " print 'object A deleted'\n"
236 " print 'object A deleted'\n"
233 "a = A()\n")
237 "a = A()\n")
234 self.mktmp(py3compat.doctest_refactor_print(src))
238 self.mktmp(py3compat.doctest_refactor_print(src))
235 if dec.module_not_available('sqlite3'):
239 if dec.module_not_available('sqlite3'):
236 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
240 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
237 else:
241 else:
238 err = None
242 err = None
239 tt.ipexec_validate(self.fname, 'object A deleted', err)
243 tt.ipexec_validate(self.fname, 'object A deleted', err)
240
244
241 @dec.skip_known_failure
245 @dec.skip_known_failure
242 def test_aggressive_namespace_cleanup(self):
246 def test_aggressive_namespace_cleanup(self):
243 """Test that namespace cleanup is not too aggressive GH-238
247 """Test that namespace cleanup is not too aggressive GH-238
244
248
245 Returning from another run magic deletes the namespace"""
249 Returning from another run magic deletes the namespace"""
246 # see ticket https://github.com/ipython/ipython/issues/238
250 # see ticket https://github.com/ipython/ipython/issues/238
247 class secondtmp(tt.TempFileMixin): pass
251 class secondtmp(tt.TempFileMixin): pass
248 empty = secondtmp()
252 empty = secondtmp()
249 empty.mktmp('')
253 empty.mktmp('')
250 src = ("ip = get_ipython()\n"
254 src = ("ip = get_ipython()\n"
251 "for i in range(5):\n"
255 "for i in range(5):\n"
252 " try:\n"
256 " try:\n"
253 " ip.magic('run %s')\n"
257 " ip.magic('run %s')\n"
254 " except NameError as e:\n"
258 " except NameError as e:\n"
255 " print i;break\n" % empty.fname)
259 " print i;break\n" % empty.fname)
256 self.mktmp(py3compat.doctest_refactor_print(src))
260 self.mktmp(py3compat.doctest_refactor_print(src))
257 _ip.magic('run %s' % self.fname)
261 _ip.magic('run %s' % self.fname)
258 _ip.run_cell('ip == get_ipython()')
262 _ip.run_cell('ip == get_ipython()')
259 nt.assert_equal(_ip.user_ns['i'], 5)
263 nt.assert_equal(_ip.user_ns['i'], 5)
260
264
261 @dec.skip_win32
265 @dec.skip_win32
262 def test_tclass(self):
266 def test_tclass(self):
263 mydir = os.path.dirname(__file__)
267 mydir = os.path.dirname(__file__)
264 tc = os.path.join(mydir, 'tclass')
268 tc = os.path.join(mydir, 'tclass')
265 src = ("%%run '%s' C-first\n"
269 src = ("%%run '%s' C-first\n"
266 "%%run '%s' C-second\n"
270 "%%run '%s' C-second\n"
267 "%%run '%s' C-third\n") % (tc, tc, tc)
271 "%%run '%s' C-third\n") % (tc, tc, tc)
268 self.mktmp(src, '.ipy')
272 self.mktmp(src, '.ipy')
269 out = """\
273 out = """\
270 ARGV 1-: ['C-first']
274 ARGV 1-: ['C-first']
271 ARGV 1-: ['C-second']
275 ARGV 1-: ['C-second']
272 tclass.py: deleting object: C-first
276 tclass.py: deleting object: C-first
273 ARGV 1-: ['C-third']
277 ARGV 1-: ['C-third']
274 tclass.py: deleting object: C-second
278 tclass.py: deleting object: C-second
275 tclass.py: deleting object: C-third
279 tclass.py: deleting object: C-third
276 """
280 """
277 if dec.module_not_available('sqlite3'):
281 if dec.module_not_available('sqlite3'):
278 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
282 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
279 else:
283 else:
280 err = None
284 err = None
281 tt.ipexec_validate(self.fname, out, err)
285 tt.ipexec_validate(self.fname, out, err)
282
286
283 def test_run_i_after_reset(self):
287 def test_run_i_after_reset(self):
284 """Check that %run -i still works after %reset (gh-693)"""
288 """Check that %run -i still works after %reset (gh-693)"""
285 src = "yy = zz\n"
289 src = "yy = zz\n"
286 self.mktmp(src)
290 self.mktmp(src)
287 _ip.run_cell("zz = 23")
291 _ip.run_cell("zz = 23")
288 _ip.magic('run -i %s' % self.fname)
292 _ip.magic('run -i %s' % self.fname)
289 nt.assert_equal(_ip.user_ns['yy'], 23)
293 nt.assert_equal(_ip.user_ns['yy'], 23)
290 _ip.magic('reset -f')
294 _ip.magic('reset -f')
291 _ip.run_cell("zz = 23")
295 _ip.run_cell("zz = 23")
292 _ip.magic('run -i %s' % self.fname)
296 _ip.magic('run -i %s' % self.fname)
293 nt.assert_equal(_ip.user_ns['yy'], 23)
297 nt.assert_equal(_ip.user_ns['yy'], 23)
294
298
295 def test_unicode(self):
299 def test_unicode(self):
296 """Check that files in odd encodings are accepted."""
300 """Check that files in odd encodings are accepted."""
297 mydir = os.path.dirname(__file__)
301 mydir = os.path.dirname(__file__)
298 na = os.path.join(mydir, 'nonascii.py')
302 na = os.path.join(mydir, 'nonascii.py')
299 _ip.magic('run "%s"' % na)
303 _ip.magic('run "%s"' % na)
300 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
304 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
301
305
302 def test_run_py_file_attribute(self):
306 def test_run_py_file_attribute(self):
303 """Test handling of `__file__` attribute in `%run <file>.py`."""
307 """Test handling of `__file__` attribute in `%run <file>.py`."""
304 src = "t = __file__\n"
308 src = "t = __file__\n"
305 self.mktmp(src)
309 self.mktmp(src)
306 _missing = object()
310 _missing = object()
307 file1 = _ip.user_ns.get('__file__', _missing)
311 file1 = _ip.user_ns.get('__file__', _missing)
308 _ip.magic('run %s' % self.fname)
312 _ip.magic('run %s' % self.fname)
309 file2 = _ip.user_ns.get('__file__', _missing)
313 file2 = _ip.user_ns.get('__file__', _missing)
310
314
311 # Check that __file__ was equal to the filename in the script's
315 # Check that __file__ was equal to the filename in the script's
312 # namespace.
316 # namespace.
313 nt.assert_equal(_ip.user_ns['t'], self.fname)
317 nt.assert_equal(_ip.user_ns['t'], self.fname)
314
318
315 # Check that __file__ was not leaked back into user_ns.
319 # Check that __file__ was not leaked back into user_ns.
316 nt.assert_equal(file1, file2)
320 nt.assert_equal(file1, file2)
317
321
318 def test_run_ipy_file_attribute(self):
322 def test_run_ipy_file_attribute(self):
319 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
323 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
320 src = "t = __file__\n"
324 src = "t = __file__\n"
321 self.mktmp(src, ext='.ipy')
325 self.mktmp(src, ext='.ipy')
322 _missing = object()
326 _missing = object()
323 file1 = _ip.user_ns.get('__file__', _missing)
327 file1 = _ip.user_ns.get('__file__', _missing)
324 _ip.magic('run %s' % self.fname)
328 _ip.magic('run %s' % self.fname)
325 file2 = _ip.user_ns.get('__file__', _missing)
329 file2 = _ip.user_ns.get('__file__', _missing)
326
330
327 # Check that __file__ was equal to the filename in the script's
331 # Check that __file__ was equal to the filename in the script's
328 # namespace.
332 # namespace.
329 nt.assert_equal(_ip.user_ns['t'], self.fname)
333 nt.assert_equal(_ip.user_ns['t'], self.fname)
330
334
331 # Check that __file__ was not leaked back into user_ns.
335 # Check that __file__ was not leaked back into user_ns.
332 nt.assert_equal(file1, file2)
336 nt.assert_equal(file1, file2)
333
337
334 def test_run_formatting(self):
338 def test_run_formatting(self):
335 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
339 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
336 src = "pass"
340 src = "pass"
337 self.mktmp(src)
341 self.mktmp(src)
338 _ip.magic('run -t -N 1 %s' % self.fname)
342 _ip.magic('run -t -N 1 %s' % self.fname)
339 _ip.magic('run -t -N 10 %s' % self.fname)
343 _ip.magic('run -t -N 10 %s' % self.fname)
344
345
346 class TestMagicRunWithPackage(unittest.TestCase):
347
348 def writefile(self, name, content):
349 path = os.path.join(self.tempdir.name, name)
350 d = os.path.dirname(path)
351 if not os.path.isdir(d):
352 os.makedirs(d)
353 with open(path, 'w') as f:
354 f.write(textwrap.dedent(content))
355
356 def setUp(self):
357 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
358 """Temporary valid python package name."""
359
360 self.value = int(random.random() * 10000)
361
362 self.tempdir = TemporaryDirectory()
363 self.__orig_cwd = os.getcwdu()
364 sys.path.insert(0, self.tempdir.name)
365
366 self.writefile(os.path.join(package, '__init__.py'), '')
367 self.writefile(os.path.join(package, 'foo.py'), """
368 x = {0!r}
369 """.format(self.value))
370 self.writefile(os.path.join(package, 'relative.py'), """
371 from .foo import x
372 """)
373 self.writefile(os.path.join(package, 'absolute.py'), """
374 from {0}.foo import x
375 """.format(package))
376
377 def tearDown(self):
378 os.chdir(self.__orig_cwd)
379 sys.path[:] = filter(lambda x: x != self.tempdir.name, sys.path)
380 self.tempdir.cleanup()
381
382 def check_run_submodule(self, submodule):
383 _ip.magic('run -m {0}.{1}'.format(self.package, submodule))
384 self.assertEqual(_ip.user_ns['x'], self.value,
385 'Variable `x` is not loaded from module `{0}`.'
386 .format(submodule))
387
388 def test_run_submodule_with_absolute_import(self):
389 self.check_run_submodule('absolute')
390
391 def test_run_submodule_with_relative_import(self):
392 """Run submodule that has a relative import statement (#2727)."""
393 self.check_run_submodule('relative')
General Comments 0
You need to be logged in to leave comments. Login now