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