##// END OF EJS Templates
test traceback offset for %run and script
MinRK -
Show More
@@ -1,476 +1,514
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 functools
17 17 import os
18 18 from os.path import join as pjoin
19 19 import random
20 20 import sys
21 21 import tempfile
22 22 import textwrap
23 23 import unittest
24 24
25 25 import nose.tools as nt
26 26 from nose import SkipTest
27 27
28 28 from IPython.testing import decorators as dec
29 29 from IPython.testing import tools as tt
30 30 from IPython.utils import py3compat
31 from IPython.utils.io import capture_output
31 32 from IPython.utils.tempdir import TemporaryDirectory
32 33 from IPython.core import debugger
33 34
34 35 #-----------------------------------------------------------------------------
35 36 # Test functions begin
36 37 #-----------------------------------------------------------------------------
37 38
38 39 def doctest_refbug():
39 40 """Very nasty problem with references held by multiple runs of a script.
40 41 See: https://github.com/ipython/ipython/issues/141
41 42
42 43 In [1]: _ip.clear_main_mod_cache()
43 44 # random
44 45
45 46 In [2]: %run refbug
46 47
47 48 In [3]: call_f()
48 49 lowercased: hello
49 50
50 51 In [4]: %run refbug
51 52
52 53 In [5]: call_f()
53 54 lowercased: hello
54 55 lowercased: hello
55 56 """
56 57
57 58
58 59 def doctest_run_builtins():
59 60 r"""Check that %run doesn't damage __builtins__.
60 61
61 62 In [1]: import tempfile
62 63
63 64 In [2]: bid1 = id(__builtins__)
64 65
65 66 In [3]: fname = tempfile.mkstemp('.py')[1]
66 67
67 68 In [3]: f = open(fname,'w')
68 69
69 70 In [4]: dummy= f.write('pass\n')
70 71
71 72 In [5]: f.flush()
72 73
73 74 In [6]: t1 = type(__builtins__)
74 75
75 76 In [7]: %run $fname
76 77
77 78 In [7]: f.close()
78 79
79 80 In [8]: bid2 = id(__builtins__)
80 81
81 82 In [9]: t2 = type(__builtins__)
82 83
83 84 In [10]: t1 == t2
84 85 Out[10]: True
85 86
86 87 In [10]: bid1 == bid2
87 88 Out[10]: True
88 89
89 90 In [12]: try:
90 91 ....: os.unlink(fname)
91 92 ....: except:
92 93 ....: pass
93 94 ....:
94 95 """
95 96
96 97
97 98 def doctest_run_option_parser():
98 99 r"""Test option parser in %run.
99 100
100 101 In [1]: %run print_argv.py
101 102 []
102 103
103 104 In [2]: %run print_argv.py print*.py
104 105 ['print_argv.py']
105 106
106 107 In [3]: %run -G print_argv.py print*.py
107 108 ['print*.py']
108 109
109 110 """
110 111
111 112
112 113 @dec.skip_win32
113 114 def doctest_run_option_parser_for_posix():
114 115 r"""Test option parser in %run (Linux/OSX specific).
115 116
116 117 You need double quote to escape glob in POSIX systems:
117 118
118 119 In [1]: %run print_argv.py print\\*.py
119 120 ['print*.py']
120 121
121 122 You can't use quote to escape glob in POSIX systems:
122 123
123 124 In [2]: %run print_argv.py 'print*.py'
124 125 ['print_argv.py']
125 126
126 127 """
127 128
128 129
129 130 @dec.skip_if_not_win32
130 131 def doctest_run_option_parser_for_windows():
131 132 r"""Test option parser in %run (Windows specific).
132 133
133 134 In Windows, you can't escape ``*` `by backslash:
134 135
135 136 In [1]: %run print_argv.py print\\*.py
136 137 ['print\\*.py']
137 138
138 139 You can use quote to escape glob:
139 140
140 141 In [2]: %run print_argv.py 'print*.py'
141 142 ['print*.py']
142 143
143 144 """
144 145
145 146
146 147 @py3compat.doctest_refactor_print
147 148 def doctest_reset_del():
148 149 """Test that resetting doesn't cause errors in __del__ methods.
149 150
150 151 In [2]: class A(object):
151 152 ...: def __del__(self):
152 153 ...: print str("Hi")
153 154 ...:
154 155
155 156 In [3]: a = A()
156 157
157 158 In [4]: get_ipython().reset()
158 159 Hi
159 160
160 161 In [5]: 1+1
161 162 Out[5]: 2
162 163 """
163 164
164 165 # For some tests, it will be handy to organize them in a class with a common
165 166 # setup that makes a temp file
166 167
167 168 class TestMagicRunPass(tt.TempFileMixin):
168 169
169 170 def setup(self):
170 171 """Make a valid python temp file."""
171 172 self.mktmp('pass\n')
172 173
173 174 def run_tmpfile(self):
174 175 _ip = get_ipython()
175 176 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
176 177 # See below and ticket https://bugs.launchpad.net/bugs/366353
177 178 _ip.magic('run %s' % self.fname)
178 179
179 180 def run_tmpfile_p(self):
180 181 _ip = get_ipython()
181 182 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 183 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 184 _ip.magic('run -p %s' % self.fname)
184 185
185 186 def test_builtins_id(self):
186 187 """Check that %run doesn't damage __builtins__ """
187 188 _ip = get_ipython()
188 189 # Test that the id of __builtins__ is not modified by %run
189 190 bid1 = id(_ip.user_ns['__builtins__'])
190 191 self.run_tmpfile()
191 192 bid2 = id(_ip.user_ns['__builtins__'])
192 193 nt.assert_equal(bid1, bid2)
193 194
194 195 def test_builtins_type(self):
195 196 """Check that the type of __builtins__ doesn't change with %run.
196 197
197 198 However, the above could pass if __builtins__ was already modified to
198 199 be a dict (it should be a module) by a previous use of %run. So we
199 200 also check explicitly that it really is a module:
200 201 """
201 202 _ip = get_ipython()
202 203 self.run_tmpfile()
203 204 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
204 205
205 206 def test_prompts(self):
206 207 """Test that prompts correctly generate after %run"""
207 208 self.run_tmpfile()
208 209 _ip = get_ipython()
209 210 p2 = _ip.prompt_manager.render('in2').strip()
210 211 nt.assert_equal(p2[:3], '...')
211 212
212 213 def test_run_profile( self ):
213 214 """Test that the option -p, which invokes the profiler, do not
214 215 crash by invoking execfile"""
215 216 _ip = get_ipython()
216 217 self.run_tmpfile_p()
217 218
218 219
219 220 class TestMagicRunSimple(tt.TempFileMixin):
220 221
221 222 def test_simpledef(self):
222 223 """Test that simple class definitions work."""
223 224 src = ("class foo: pass\n"
224 225 "def f(): return foo()")
225 226 self.mktmp(src)
226 227 _ip.magic('run %s' % self.fname)
227 228 _ip.run_cell('t = isinstance(f(), foo)')
228 229 nt.assert_true(_ip.user_ns['t'])
229 230
230 231 def test_obj_del(self):
231 232 """Test that object's __del__ methods are called on exit."""
232 233 if sys.platform == 'win32':
233 234 try:
234 235 import win32api
235 236 except ImportError:
236 237 raise SkipTest("Test requires pywin32")
237 238 src = ("class A(object):\n"
238 239 " def __del__(self):\n"
239 240 " print 'object A deleted'\n"
240 241 "a = A()\n")
241 242 self.mktmp(py3compat.doctest_refactor_print(src))
242 243 if dec.module_not_available('sqlite3'):
243 244 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
244 245 else:
245 246 err = None
246 247 tt.ipexec_validate(self.fname, 'object A deleted', err)
247 248
248 249 def test_aggressive_namespace_cleanup(self):
249 250 """Test that namespace cleanup is not too aggressive GH-238
250 251
251 252 Returning from another run magic deletes the namespace"""
252 253 # see ticket https://github.com/ipython/ipython/issues/238
253 254 class secondtmp(tt.TempFileMixin): pass
254 255 empty = secondtmp()
255 256 empty.mktmp('')
256 257 # On Windows, the filename will have \users in it, so we need to use the
257 258 # repr so that the \u becomes \\u.
258 259 src = ("ip = get_ipython()\n"
259 260 "for i in range(5):\n"
260 261 " try:\n"
261 262 " ip.magic(%r)\n"
262 263 " except NameError as e:\n"
263 264 " print(i)\n"
264 265 " break\n" % ('run ' + empty.fname))
265 266 self.mktmp(src)
266 267 _ip.magic('run %s' % self.fname)
267 268 _ip.run_cell('ip == get_ipython()')
268 269 nt.assert_equal(_ip.user_ns['i'], 4)
269 270
270 271 def test_run_second(self):
271 272 """Test that running a second file doesn't clobber the first, gh-3547
272 273 """
273 274 self.mktmp("avar = 1\n"
274 275 "def afunc():\n"
275 276 " return avar\n")
276 277
277 278 empty = tt.TempFileMixin()
278 279 empty.mktmp("")
279 280
280 281 _ip.magic('run %s' % self.fname)
281 282 _ip.magic('run %s' % empty.fname)
282 283 nt.assert_equal(_ip.user_ns['afunc'](), 1)
283 284
284 285 @dec.skip_win32
285 286 def test_tclass(self):
286 287 mydir = os.path.dirname(__file__)
287 288 tc = os.path.join(mydir, 'tclass')
288 289 src = ("%%run '%s' C-first\n"
289 290 "%%run '%s' C-second\n"
290 291 "%%run '%s' C-third\n") % (tc, tc, tc)
291 292 self.mktmp(src, '.ipy')
292 293 out = """\
293 294 ARGV 1-: ['C-first']
294 295 ARGV 1-: ['C-second']
295 296 tclass.py: deleting object: C-first
296 297 ARGV 1-: ['C-third']
297 298 tclass.py: deleting object: C-second
298 299 tclass.py: deleting object: C-third
299 300 """
300 301 if dec.module_not_available('sqlite3'):
301 302 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
302 303 else:
303 304 err = None
304 305 tt.ipexec_validate(self.fname, out, err)
305 306
306 307 def test_run_i_after_reset(self):
307 308 """Check that %run -i still works after %reset (gh-693)"""
308 309 src = "yy = zz\n"
309 310 self.mktmp(src)
310 311 _ip.run_cell("zz = 23")
311 312 _ip.magic('run -i %s' % self.fname)
312 313 nt.assert_equal(_ip.user_ns['yy'], 23)
313 314 _ip.magic('reset -f')
314 315 _ip.run_cell("zz = 23")
315 316 _ip.magic('run -i %s' % self.fname)
316 317 nt.assert_equal(_ip.user_ns['yy'], 23)
317 318
318 319 def test_unicode(self):
319 320 """Check that files in odd encodings are accepted."""
320 321 mydir = os.path.dirname(__file__)
321 322 na = os.path.join(mydir, 'nonascii.py')
322 323 _ip.magic('run "%s"' % na)
323 324 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
324 325
325 326 def test_run_py_file_attribute(self):
326 327 """Test handling of `__file__` attribute in `%run <file>.py`."""
327 328 src = "t = __file__\n"
328 329 self.mktmp(src)
329 330 _missing = object()
330 331 file1 = _ip.user_ns.get('__file__', _missing)
331 332 _ip.magic('run %s' % self.fname)
332 333 file2 = _ip.user_ns.get('__file__', _missing)
333 334
334 335 # Check that __file__ was equal to the filename in the script's
335 336 # namespace.
336 337 nt.assert_equal(_ip.user_ns['t'], self.fname)
337 338
338 339 # Check that __file__ was not leaked back into user_ns.
339 340 nt.assert_equal(file1, file2)
340 341
341 342 def test_run_ipy_file_attribute(self):
342 343 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
343 344 src = "t = __file__\n"
344 345 self.mktmp(src, ext='.ipy')
345 346 _missing = object()
346 347 file1 = _ip.user_ns.get('__file__', _missing)
347 348 _ip.magic('run %s' % self.fname)
348 349 file2 = _ip.user_ns.get('__file__', _missing)
349 350
350 351 # Check that __file__ was equal to the filename in the script's
351 352 # namespace.
352 353 nt.assert_equal(_ip.user_ns['t'], self.fname)
353 354
354 355 # Check that __file__ was not leaked back into user_ns.
355 356 nt.assert_equal(file1, file2)
356 357
357 358 def test_run_formatting(self):
358 359 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
359 360 src = "pass"
360 361 self.mktmp(src)
361 362 _ip.magic('run -t -N 1 %s' % self.fname)
362 363 _ip.magic('run -t -N 10 %s' % self.fname)
363 364
364 365 def test_ignore_sys_exit(self):
365 366 """Test the -e option to ignore sys.exit()"""
366 367 src = "import sys; sys.exit(1)"
367 368 self.mktmp(src)
368 369 with tt.AssertPrints('SystemExit'):
369 370 _ip.magic('run %s' % self.fname)
370 371
371 372 with tt.AssertNotPrints('SystemExit'):
372 373 _ip.magic('run -e %s' % self.fname)
373 374
374 375 def test_run_nb(self):
375 376 """Test %run notebook.ipynb"""
376 377 from IPython.nbformat import current
377 378 nb = current.new_notebook(
378 379 worksheets=[
379 380 current.new_worksheet(cells=[
380 381 current.new_text_cell("The Ultimate Question of Everything"),
381 382 current.new_code_cell("answer=42")
382 383 ])
383 384 ]
384 385 )
385 386 src = current.writes(nb, 'json')
386 387 self.mktmp(src, ext='.ipynb')
387 388
388 389 _ip.magic("run %s" % self.fname)
389 390
390 391 nt.assert_equal(_ip.user_ns['answer'], 42)
391 392
392 393
393 394
394 395 class TestMagicRunWithPackage(unittest.TestCase):
395 396
396 397 def writefile(self, name, content):
397 398 path = os.path.join(self.tempdir.name, name)
398 399 d = os.path.dirname(path)
399 400 if not os.path.isdir(d):
400 401 os.makedirs(d)
401 402 with open(path, 'w') as f:
402 403 f.write(textwrap.dedent(content))
403 404
404 405 def setUp(self):
405 406 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
406 407 """Temporary valid python package name."""
407 408
408 409 self.value = int(random.random() * 10000)
409 410
410 411 self.tempdir = TemporaryDirectory()
411 412 self.__orig_cwd = py3compat.getcwd()
412 413 sys.path.insert(0, self.tempdir.name)
413 414
414 415 self.writefile(os.path.join(package, '__init__.py'), '')
415 416 self.writefile(os.path.join(package, 'sub.py'), """
416 417 x = {0!r}
417 418 """.format(self.value))
418 419 self.writefile(os.path.join(package, 'relative.py'), """
419 420 from .sub import x
420 421 """)
421 422 self.writefile(os.path.join(package, 'absolute.py'), """
422 423 from {0}.sub import x
423 424 """.format(package))
424 425
425 426 def tearDown(self):
426 427 os.chdir(self.__orig_cwd)
427 428 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
428 429 self.tempdir.cleanup()
429 430
430 431 def check_run_submodule(self, submodule, opts=''):
431 432 _ip.user_ns.pop('x', None)
432 433 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
433 434 self.assertEqual(_ip.user_ns['x'], self.value,
434 435 'Variable `x` is not loaded from module `{0}`.'
435 436 .format(submodule))
436 437
437 438 def test_run_submodule_with_absolute_import(self):
438 439 self.check_run_submodule('absolute')
439 440
440 441 def test_run_submodule_with_relative_import(self):
441 442 """Run submodule that has a relative import statement (#2727)."""
442 443 self.check_run_submodule('relative')
443 444
444 445 def test_prun_submodule_with_absolute_import(self):
445 446 self.check_run_submodule('absolute', '-p')
446 447
447 448 def test_prun_submodule_with_relative_import(self):
448 449 self.check_run_submodule('relative', '-p')
449 450
450 451 def with_fake_debugger(func):
451 452 @functools.wraps(func)
452 453 def wrapper(*args, **kwds):
453 454 with tt.monkeypatch(debugger.Pdb, 'run', staticmethod(eval)):
454 455 return func(*args, **kwds)
455 456 return wrapper
456 457
457 458 @with_fake_debugger
458 459 def test_debug_run_submodule_with_absolute_import(self):
459 460 self.check_run_submodule('absolute', '-d')
460 461
461 462 @with_fake_debugger
462 463 def test_debug_run_submodule_with_relative_import(self):
463 464 self.check_run_submodule('relative', '-d')
464 465
465 466 def test_run__name__():
466 467 with TemporaryDirectory() as td:
467 468 path = pjoin(td, 'foo.py')
468 469 with open(path, 'w') as f:
469 470 f.write("q = __name__")
470 471
471 472 _ip.user_ns.pop('q', None)
472 473 _ip.magic('run {}'.format(path))
473 474 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
474 475
475 476 _ip.magic('run -n {}'.format(path))
476 477 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
478
479 def test_run_tb():
480 """Test traceback offset in %run"""
481 with TemporaryDirectory() as td:
482 path = pjoin(td, 'foo.py')
483 with open(path, 'w') as f:
484 f.write('\n'.join([
485 "def foo():",
486 " return bar()",
487 "def bar():",
488 " raise RuntimeError('hello!')",
489 "foo()",
490 ]))
491 with capture_output() as io:
492 _ip.magic('run {}'.format(path))
493 out = io.stdout
494 nt.assert_not_in("execfile", out)
495 nt.assert_in("RuntimeError", out)
496 nt.assert_equal(out.count("---->"), 3)
497
498 def test_script_tb():
499 """Test traceback offset in `ipython script.py`"""
500 with TemporaryDirectory() as td:
501 path = pjoin(td, 'foo.py')
502 with open(path, 'w') as f:
503 f.write('\n'.join([
504 "def foo():",
505 " return bar()",
506 "def bar():",
507 " raise RuntimeError('hello!')",
508 "foo()",
509 ]))
510 out, err = tt.ipexec(path)
511 nt.assert_not_in("execfile", out)
512 nt.assert_in("RuntimeError", out)
513 nt.assert_equal(out.count("---->"), 3)
514
General Comments 0
You need to be logged in to leave comments. Login now