##// END OF EJS Templates
revert fialing on windows
Matthias Bussonnier -
Show More
@@ -1,626 +1,626 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 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 11 as otherwise it may influence later tests.
12 12 """
13 13
14 14 # Copyright (c) IPython Development Team.
15 15 # Distributed under the terms of the Modified BSD License.
16 16
17 17
18 18
19 19 import functools
20 20 import os
21 21 import platform
22 22 import random
23 23 import string
24 24 import sys
25 25 import textwrap
26 26 import unittest
27 27 from os.path import join as pjoin
28 28 from unittest.mock import patch
29 29
30 30 import pytest
31 31 from tempfile import TemporaryDirectory
32 32
33 33 from IPython.core import debugger
34 34 from IPython.testing import decorators as dec
35 35 from IPython.testing import tools as tt
36 36 from IPython.utils.io import capture_output
37 37
38 38
39 39 def doctest_refbug():
40 40 """Very nasty problem with references held by multiple runs of a script.
41 41 See: https://github.com/ipython/ipython/issues/141
42 42
43 43 In [1]: _ip.clear_main_mod_cache()
44 44 # random
45 45
46 46 In [2]: %run refbug
47 47
48 48 In [3]: call_f()
49 49 lowercased: hello
50 50
51 51 In [4]: %run refbug
52 52
53 53 In [5]: call_f()
54 54 lowercased: hello
55 55 lowercased: hello
56 56 """
57 57
58 58
59 59 def doctest_run_builtins():
60 60 r"""Check that %run doesn't damage __builtins__.
61 61
62 62 In [1]: import tempfile
63 63
64 64 In [2]: bid1 = id(__builtins__)
65 65
66 66 In [3]: fname = tempfile.mkstemp('.py')[1]
67 67
68 68 In [3]: f = open(fname, 'w', encoding='utf-8')
69 69
70 70 In [4]: dummy= f.write('pass\n')
71 71
72 72 In [5]: f.flush()
73 73
74 74 In [6]: t1 = type(__builtins__)
75 75
76 76 In [7]: %run $fname
77 77
78 78 In [7]: f.close()
79 79
80 80 In [8]: bid2 = id(__builtins__)
81 81
82 82 In [9]: t2 = type(__builtins__)
83 83
84 84 In [10]: t1 == t2
85 85 Out[10]: True
86 86
87 87 In [10]: bid1 == bid2
88 88 Out[10]: True
89 89
90 90 In [12]: try:
91 91 ....: os.unlink(fname)
92 92 ....: except:
93 93 ....: pass
94 94 ....:
95 95 """
96 96
97 97
98 98 def doctest_run_option_parser():
99 99 r"""Test option parser in %run.
100 100
101 101 In [1]: %run print_argv.py
102 102 []
103 103
104 104 In [2]: %run print_argv.py print*.py
105 105 ['print_argv.py']
106 106
107 107 In [3]: %run -G print_argv.py print*.py
108 108 ['print*.py']
109 109
110 110 """
111 111
112 112
113 113 @dec.skip_win32
114 114 def doctest_run_option_parser_for_posix():
115 115 r"""Test option parser in %run (Linux/OSX specific).
116 116
117 117 You need double quote to escape glob in POSIX systems:
118 118
119 119 In [1]: %run print_argv.py print\\*.py
120 120 ['print*.py']
121 121
122 122 You can't use quote to escape glob in POSIX systems:
123 123
124 124 In [2]: %run print_argv.py 'print*.py'
125 125 ['print_argv.py']
126 126
127 127 """
128 128
129 129
130 130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
131 131
132 132
133 133 @dec.skip_if_not_win32
134 134 def doctest_run_option_parser_for_windows():
135 135 r"""Test option parser in %run (Windows specific).
136 136
137 137 In Windows, you can't escape ``*` `by backslash:
138 138
139 139 In [1]: %run print_argv.py print\\*.py
140 140 ['print\\\\*.py']
141 141
142 142 You can use quote to escape glob:
143 143
144 144 In [2]: %run print_argv.py 'print*.py'
145 145 ["'print*.py'"]
146 146
147 147 """
148 148
149 149
150 150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
151 151
152 152
153 153 def doctest_reset_del():
154 154 """Test that resetting doesn't cause errors in __del__ methods.
155 155
156 156 In [2]: class A(object):
157 157 ...: def __del__(self):
158 158 ...: print(str("Hi"))
159 159 ...:
160 160
161 161 In [3]: a = A()
162 162
163 163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
164 164 Hi
165 165
166 166 In [5]: 1+1
167 167 Out[5]: 2
168 168 """
169 169
170 170 # For some tests, it will be handy to organize them in a class with a common
171 171 # setup that makes a temp file
172 172
173 173 class TestMagicRunPass(tt.TempFileMixin):
174 174
175 175 def setUp(self):
176 176 content = "a = [1,2,3]\nb = 1"
177 177 self.mktmp(content)
178 178
179 179 def run_tmpfile(self):
180 180 _ip = get_ipython()
181 181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 182 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 183 _ip.run_line_magic("run", self.fname)
184 184
185 185 def run_tmpfile_p(self):
186 186 _ip = get_ipython()
187 187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
188 188 # See below and ticket https://bugs.launchpad.net/bugs/366353
189 189 _ip.run_line_magic("run", "-p %s" % self.fname)
190 190
191 191 def test_builtins_id(self):
192 192 """Check that %run doesn't damage __builtins__ """
193 193 _ip = get_ipython()
194 194 # Test that the id of __builtins__ is not modified by %run
195 195 bid1 = id(_ip.user_ns['__builtins__'])
196 196 self.run_tmpfile()
197 197 bid2 = id(_ip.user_ns['__builtins__'])
198 198 assert bid1 == bid2
199 199
200 200 def test_builtins_type(self):
201 201 """Check that the type of __builtins__ doesn't change with %run.
202 202
203 203 However, the above could pass if __builtins__ was already modified to
204 204 be a dict (it should be a module) by a previous use of %run. So we
205 205 also check explicitly that it really is a module:
206 206 """
207 207 _ip = get_ipython()
208 208 self.run_tmpfile()
209 209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
210 210
211 211 def test_run_profile(self):
212 212 """Test that the option -p, which invokes the profiler, do not
213 213 crash by invoking execfile"""
214 214 self.run_tmpfile_p()
215 215
216 216 def test_run_debug_twice(self):
217 217 # https://github.com/ipython/ipython/issues/10028
218 218 _ip = get_ipython()
219 219 with tt.fake_input(["c"]):
220 220 _ip.run_line_magic("run", "-d %s" % self.fname)
221 221 with tt.fake_input(["c"]):
222 222 _ip.run_line_magic("run", "-d %s" % self.fname)
223 223
224 224 def test_run_debug_twice_with_breakpoint(self):
225 225 """Make a valid python temp file."""
226 226 _ip = get_ipython()
227 227 with tt.fake_input(["b 2", "c", "c"]):
228 228 _ip.run_line_magic("run", "-d %s" % self.fname)
229 229
230 230 with tt.fake_input(["c"]):
231 231 with tt.AssertNotPrints("KeyError"):
232 232 _ip.run_line_magic("run", "-d %s" % self.fname)
233 233
234 234
235 235 class TestMagicRunSimple(tt.TempFileMixin):
236 236
237 237 def test_simpledef(self):
238 238 """Test that simple class definitions work."""
239 239 src = ("class foo: pass\n"
240 240 "def f(): return foo()")
241 241 self.mktmp(src)
242 242 _ip.run_line_magic("run", str(self.fname))
243 243 _ip.run_cell("t = isinstance(f(), foo)")
244 244 assert _ip.user_ns["t"] is True
245 245
246 246 @pytest.mark.xfail(
247 247 platform.python_implementation() == "PyPy",
248 248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
249 249 )
250 250 def test_obj_del(self):
251 251 """Test that object's __del__ methods are called on exit."""
252 252 src = ("class A(object):\n"
253 253 " def __del__(self):\n"
254 254 " print('object A deleted')\n"
255 255 "a = A()\n")
256 256 self.mktmp(src)
257 257 err = None
258 258 tt.ipexec_validate(self.fname, 'object A deleted', err)
259 259
260 260 def test_aggressive_namespace_cleanup(self):
261 261 """Test that namespace cleanup is not too aggressive GH-238
262 262
263 263 Returning from another run magic deletes the namespace"""
264 264 # see ticket https://github.com/ipython/ipython/issues/238
265 265
266 266 with tt.TempFileMixin() as empty:
267 267 empty.mktmp("")
268 268 # On Windows, the filename will have \users in it, so we need to use the
269 269 # repr so that the \u becomes \\u.
270 270 src = (
271 271 "ip = get_ipython()\n"
272 272 "for i in range(5):\n"
273 273 " try:\n"
274 274 " ip.magic(%r)\n"
275 275 " except NameError as e:\n"
276 276 " print(i)\n"
277 277 " break\n" % ("run " + empty.fname)
278 278 )
279 279 self.mktmp(src)
280 280 _ip.run_line_magic("run", str(self.fname))
281 281 _ip.run_cell("ip == get_ipython()")
282 282 assert _ip.user_ns["i"] == 4
283 283
284 284 def test_run_second(self):
285 285 """Test that running a second file doesn't clobber the first, gh-3547"""
286 286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
287 287
288 288 with tt.TempFileMixin() as empty:
289 289 empty.mktmp("")
290 290
291 291 _ip.run_line_magic("run", self.fname)
292 292 _ip.run_line_magic("run", empty.fname)
293 293 assert _ip.user_ns["afunc"]() == 1
294 294
295 295 def test_tclass(self):
296 296 mydir = os.path.dirname(__file__)
297 297 tc = os.path.join(mydir, "tclass")
298 298 src = f"""\
299 299 import gc
300 300 %run "{tc}" C-first
301 301 gc.collect(0)
302 302 %run "{tc}" C-second
303 303 gc.collect(0)
304 304 %run "{tc}" C-third
305 305 gc.collect(0)
306 306 %reset -f
307 307 """
308 308 self.mktmp(src, ".ipy")
309 309 out = """\
310 310 ARGV 1-: ['C-first']
311 311 ARGV 1-: ['C-second']
312 312 tclass.py: deleting object: C-first
313 313 ARGV 1-: ['C-third']
314 314 tclass.py: deleting object: C-second
315 315 tclass.py: deleting object: C-third
316 316 """
317 317 err = None
318 318 tt.ipexec_validate(self.fname, out, err)
319 319
320 320 def test_run_i_after_reset(self):
321 321 """Check that %run -i still works after %reset (gh-693)"""
322 322 src = "yy = zz\n"
323 323 self.mktmp(src)
324 324 _ip.run_cell("zz = 23")
325 325 try:
326 326 _ip.run_line_magic("run", "-i %s" % self.fname)
327 327 assert _ip.user_ns["yy"] == 23
328 328 finally:
329 329 _ip.run_line_magic("reset", "-f")
330 330
331 331 _ip.run_cell("zz = 23")
332 332 try:
333 333 _ip.run_line_magic("run", "-i %s" % self.fname)
334 334 assert _ip.user_ns["yy"] == 23
335 335 finally:
336 336 _ip.run_line_magic("reset", "-f")
337 337
338 338 def test_unicode(self):
339 339 """Check that files in odd encodings are accepted."""
340 340 mydir = os.path.dirname(__file__)
341 341 na = os.path.join(mydir, "nonascii.py")
342 _ip.run_line_magic("run", "%r" % na)
342 _ip.magic('run "%s"' % na)
343 343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
344 344
345 345 def test_run_py_file_attribute(self):
346 346 """Test handling of `__file__` attribute in `%run <file>.py`."""
347 347 src = "t = __file__\n"
348 348 self.mktmp(src)
349 349 _missing = object()
350 350 file1 = _ip.user_ns.get("__file__", _missing)
351 351 _ip.run_line_magic("run", self.fname)
352 352 file2 = _ip.user_ns.get("__file__", _missing)
353 353
354 354 # Check that __file__ was equal to the filename in the script's
355 355 # namespace.
356 356 assert _ip.user_ns["t"] == self.fname
357 357
358 358 # Check that __file__ was not leaked back into user_ns.
359 359 assert file1 == file2
360 360
361 361 def test_run_ipy_file_attribute(self):
362 362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
363 363 src = "t = __file__\n"
364 364 self.mktmp(src, ext='.ipy')
365 365 _missing = object()
366 366 file1 = _ip.user_ns.get("__file__", _missing)
367 367 _ip.run_line_magic("run", self.fname)
368 368 file2 = _ip.user_ns.get("__file__", _missing)
369 369
370 370 # Check that __file__ was equal to the filename in the script's
371 371 # namespace.
372 372 assert _ip.user_ns["t"] == self.fname
373 373
374 374 # Check that __file__ was not leaked back into user_ns.
375 375 assert file1 == file2
376 376
377 377 def test_run_formatting(self):
378 378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
379 379 src = "pass"
380 380 self.mktmp(src)
381 381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
382 382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
383 383
384 384 def test_ignore_sys_exit(self):
385 385 """Test the -e option to ignore sys.exit()"""
386 386 src = "import sys; sys.exit(1)"
387 387 self.mktmp(src)
388 388 with tt.AssertPrints("SystemExit"):
389 389 _ip.run_line_magic("run", self.fname)
390 390
391 391 with tt.AssertNotPrints("SystemExit"):
392 392 _ip.run_line_magic("run", "-e %s" % self.fname)
393 393
394 394 def test_run_nb(self):
395 395 """Test %run notebook.ipynb"""
396 396 pytest.importorskip("nbformat")
397 397 from nbformat import v4, writes
398 398 nb = v4.new_notebook(
399 399 cells=[
400 400 v4.new_markdown_cell("The Ultimate Question of Everything"),
401 401 v4.new_code_cell("answer=42")
402 402 ]
403 403 )
404 404 src = writes(nb, version=4)
405 405 self.mktmp(src, ext='.ipynb')
406 406
407 407 _ip.run_line_magic("run", self.fname)
408 408
409 409 assert _ip.user_ns["answer"] == 42
410 410
411 411 def test_run_nb_error(self):
412 412 """Test %run notebook.ipynb error"""
413 413 pytest.importorskip("nbformat")
414 414 from nbformat import v4, writes
415 415
416 416 # %run when a file name isn't provided
417 417 pytest.raises(Exception, _ip.magic, "run")
418 418
419 419 # %run when a file doesn't exist
420 420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
421 421
422 422 # %run on a notebook with an error
423 423 nb = v4.new_notebook(
424 424 cells=[
425 425 v4.new_code_cell("0/0")
426 426 ]
427 427 )
428 428 src = writes(nb, version=4)
429 429 self.mktmp(src, ext='.ipynb')
430 430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
431 431
432 432 def test_file_options(self):
433 433 src = ('import sys\n'
434 434 'a = " ".join(sys.argv[1:])\n')
435 435 self.mktmp(src)
436 436 test_opts = "-x 3 --verbose"
437 437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
438 438 assert _ip.user_ns["a"] == test_opts
439 439
440 440
441 441 class TestMagicRunWithPackage(unittest.TestCase):
442 442
443 443 def writefile(self, name, content):
444 444 path = os.path.join(self.tempdir.name, name)
445 445 d = os.path.dirname(path)
446 446 if not os.path.isdir(d):
447 447 os.makedirs(d)
448 448 with open(path, "w", encoding="utf-8") as f:
449 449 f.write(textwrap.dedent(content))
450 450
451 451 def setUp(self):
452 452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
453 453 """Temporary (probably) valid python package name."""
454 454
455 455 self.value = int(random.random() * 10000)
456 456
457 457 self.tempdir = TemporaryDirectory()
458 458 self.__orig_cwd = os.getcwd()
459 459 sys.path.insert(0, self.tempdir.name)
460 460
461 461 self.writefile(os.path.join(package, '__init__.py'), '')
462 462 self.writefile(os.path.join(package, 'sub.py'), """
463 463 x = {0!r}
464 464 """.format(self.value))
465 465 self.writefile(os.path.join(package, 'relative.py'), """
466 466 from .sub import x
467 467 """)
468 468 self.writefile(os.path.join(package, 'absolute.py'), """
469 469 from {0}.sub import x
470 470 """.format(package))
471 471 self.writefile(os.path.join(package, 'args.py'), """
472 472 import sys
473 473 a = " ".join(sys.argv[1:])
474 474 """.format(package))
475 475
476 476 def tearDown(self):
477 477 os.chdir(self.__orig_cwd)
478 478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
479 479 self.tempdir.cleanup()
480 480
481 481 def check_run_submodule(self, submodule, opts=""):
482 482 _ip.user_ns.pop("x", None)
483 483 _ip.run_line_magic(
484 484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
485 485 )
486 486 self.assertEqual(
487 487 _ip.user_ns["x"],
488 488 self.value,
489 489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
490 490 )
491 491
492 492 def test_run_submodule_with_absolute_import(self):
493 493 self.check_run_submodule('absolute')
494 494
495 495 def test_run_submodule_with_relative_import(self):
496 496 """Run submodule that has a relative import statement (#2727)."""
497 497 self.check_run_submodule('relative')
498 498
499 499 def test_prun_submodule_with_absolute_import(self):
500 500 self.check_run_submodule('absolute', '-p')
501 501
502 502 def test_prun_submodule_with_relative_import(self):
503 503 self.check_run_submodule('relative', '-p')
504 504
505 505 def with_fake_debugger(func):
506 506 @functools.wraps(func)
507 507 def wrapper(*args, **kwds):
508 508 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
509 509 return func(*args, **kwds)
510 510 return wrapper
511 511
512 512 @with_fake_debugger
513 513 def test_debug_run_submodule_with_absolute_import(self):
514 514 self.check_run_submodule('absolute', '-d')
515 515
516 516 @with_fake_debugger
517 517 def test_debug_run_submodule_with_relative_import(self):
518 518 self.check_run_submodule('relative', '-d')
519 519
520 520 def test_module_options(self):
521 521 _ip.user_ns.pop("a", None)
522 522 test_opts = "-x abc -m test"
523 523 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
524 524 assert _ip.user_ns["a"] == test_opts
525 525
526 526 def test_module_options_with_separator(self):
527 527 _ip.user_ns.pop("a", None)
528 528 test_opts = "-x abc -m test"
529 529 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
530 530 assert _ip.user_ns["a"] == test_opts
531 531
532 532
533 533 def test_run__name__():
534 534 with TemporaryDirectory() as td:
535 535 path = pjoin(td, "foo.py")
536 536 with open(path, "w", encoding="utf-8") as f:
537 537 f.write("q = __name__")
538 538
539 539 _ip.user_ns.pop("q", None)
540 540 _ip.run_line_magic("run", "{}".format(path))
541 541 assert _ip.user_ns.pop("q") == "__main__"
542 542
543 543 _ip.run_line_magic("run", "-n {}".format(path))
544 544 assert _ip.user_ns.pop("q") == "foo"
545 545
546 546 try:
547 547 _ip.run_line_magic("run", "-i -n {}".format(path))
548 548 assert _ip.user_ns.pop("q") == "foo"
549 549 finally:
550 550 _ip.run_line_magic("reset", "-f")
551 551
552 552
553 553 def test_run_tb():
554 554 """Test traceback offset in %run"""
555 555 with TemporaryDirectory() as td:
556 556 path = pjoin(td, "foo.py")
557 557 with open(path, "w", encoding="utf-8") as f:
558 558 f.write(
559 559 "\n".join(
560 560 [
561 561 "def foo():",
562 562 " return bar()",
563 563 "def bar():",
564 564 " raise RuntimeError('hello!')",
565 565 "foo()",
566 566 ]
567 567 )
568 568 )
569 569 with capture_output() as io:
570 570 _ip.run_line_magic("run", "{}".format(path))
571 571 out = io.stdout
572 572 assert "execfile" not in out
573 573 assert "RuntimeError" in out
574 574 assert out.count("---->") == 3
575 575 del ip.user_ns['bar']
576 576 del ip.user_ns['foo']
577 577
578 578
579 579 def test_multiprocessing_run():
580 580 """Set we can run mutiprocesgin without messing up up main namespace
581 581
582 582 Note that import `nose.tools as nt` mdify the value s
583 583 sys.module['__mp_main__'] so we need to temporarily set it to None to test
584 584 the issue.
585 585 """
586 586 with TemporaryDirectory() as td:
587 587 mpm = sys.modules.get('__mp_main__')
588 588 sys.modules['__mp_main__'] = None
589 589 try:
590 590 path = pjoin(td, "test.py")
591 591 with open(path, "w", encoding="utf-8") as f:
592 592 f.write("import multiprocessing\nprint('hoy')")
593 593 with capture_output() as io:
594 594 _ip.run_line_magic('run', path)
595 595 _ip.run_cell("i_m_undefined")
596 596 out = io.stdout
597 597 assert "hoy" in out
598 598 assert "AttributeError" not in out
599 599 assert "NameError" in out
600 600 assert out.count("---->") == 1
601 601 except:
602 602 raise
603 603 finally:
604 604 sys.modules['__mp_main__'] = mpm
605 605
606 606
607 607 def test_script_tb():
608 608 """Test traceback offset in `ipython script.py`"""
609 609 with TemporaryDirectory() as td:
610 610 path = pjoin(td, "foo.py")
611 611 with open(path, "w", encoding="utf-8") as f:
612 612 f.write(
613 613 "\n".join(
614 614 [
615 615 "def foo():",
616 616 " return bar()",
617 617 "def bar():",
618 618 " raise RuntimeError('hello!')",
619 619 "foo()",
620 620 ]
621 621 )
622 622 )
623 623 out, err = tt.ipexec(path)
624 624 assert "execfile" not in out
625 625 assert "RuntimeError" in out
626 626 assert out.count("---->") == 3
General Comments 0
You need to be logged in to leave comments. Login now