##// END OF EJS Templates
Add failing test for running %run -d twice
Thomas Kluyver -
Show More
@@ -1,507 +1,514 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
11 11 # Copyright (c) IPython Development Team.
12 12 # Distributed under the terms of the Modified BSD License.
13 13
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 textwrap
22 22 import unittest
23 23
24 24 try:
25 25 from unittest.mock import patch
26 26 except ImportError:
27 27 from mock import patch
28 28
29 29 import nose.tools as nt
30 30 from nose import SkipTest
31 31
32 32 from IPython.testing import decorators as dec
33 33 from IPython.testing import tools as tt
34 34 from IPython.utils import py3compat
35 35 from IPython.utils.io import capture_output
36 36 from IPython.utils.tempdir import TemporaryDirectory
37 37 from IPython.core import debugger
38 38
39 39
40 40 def doctest_refbug():
41 41 """Very nasty problem with references held by multiple runs of a script.
42 42 See: https://github.com/ipython/ipython/issues/141
43 43
44 44 In [1]: _ip.clear_main_mod_cache()
45 45 # random
46 46
47 47 In [2]: %run refbug
48 48
49 49 In [3]: call_f()
50 50 lowercased: hello
51 51
52 52 In [4]: %run refbug
53 53
54 54 In [5]: call_f()
55 55 lowercased: hello
56 56 lowercased: hello
57 57 """
58 58
59 59
60 60 def doctest_run_builtins():
61 61 r"""Check that %run doesn't damage __builtins__.
62 62
63 63 In [1]: import tempfile
64 64
65 65 In [2]: bid1 = id(__builtins__)
66 66
67 67 In [3]: fname = tempfile.mkstemp('.py')[1]
68 68
69 69 In [3]: f = open(fname,'w')
70 70
71 71 In [4]: dummy= f.write('pass\n')
72 72
73 73 In [5]: f.flush()
74 74
75 75 In [6]: t1 = type(__builtins__)
76 76
77 77 In [7]: %run $fname
78 78
79 79 In [7]: f.close()
80 80
81 81 In [8]: bid2 = id(__builtins__)
82 82
83 83 In [9]: t2 = type(__builtins__)
84 84
85 85 In [10]: t1 == t2
86 86 Out[10]: True
87 87
88 88 In [10]: bid1 == bid2
89 89 Out[10]: True
90 90
91 91 In [12]: try:
92 92 ....: os.unlink(fname)
93 93 ....: except:
94 94 ....: pass
95 95 ....:
96 96 """
97 97
98 98
99 99 def doctest_run_option_parser():
100 100 r"""Test option parser in %run.
101 101
102 102 In [1]: %run print_argv.py
103 103 []
104 104
105 105 In [2]: %run print_argv.py print*.py
106 106 ['print_argv.py']
107 107
108 108 In [3]: %run -G print_argv.py print*.py
109 109 ['print*.py']
110 110
111 111 """
112 112
113 113
114 114 @dec.skip_win32
115 115 def doctest_run_option_parser_for_posix():
116 116 r"""Test option parser in %run (Linux/OSX specific).
117 117
118 118 You need double quote to escape glob in POSIX systems:
119 119
120 120 In [1]: %run print_argv.py print\\*.py
121 121 ['print*.py']
122 122
123 123 You can't use quote to escape glob in POSIX systems:
124 124
125 125 In [2]: %run print_argv.py 'print*.py'
126 126 ['print_argv.py']
127 127
128 128 """
129 129
130 130
131 131 @dec.skip_if_not_win32
132 132 def doctest_run_option_parser_for_windows():
133 133 r"""Test option parser in %run (Windows specific).
134 134
135 135 In Windows, you can't escape ``*` `by backslash:
136 136
137 137 In [1]: %run print_argv.py print\\*.py
138 138 ['print\\*.py']
139 139
140 140 You can use quote to escape glob:
141 141
142 142 In [2]: %run print_argv.py 'print*.py'
143 143 ['print*.py']
144 144
145 145 """
146 146
147 147
148 148 @py3compat.doctest_refactor_print
149 149 def doctest_reset_del():
150 150 """Test that resetting doesn't cause errors in __del__ methods.
151 151
152 152 In [2]: class A(object):
153 153 ...: def __del__(self):
154 154 ...: print str("Hi")
155 155 ...:
156 156
157 157 In [3]: a = A()
158 158
159 159 In [4]: get_ipython().reset()
160 160 Hi
161 161
162 162 In [5]: 1+1
163 163 Out[5]: 2
164 164 """
165 165
166 166 # For some tests, it will be handy to organize them in a class with a common
167 167 # setup that makes a temp file
168 168
169 169 class TestMagicRunPass(tt.TempFileMixin):
170 170
171 171 def setup(self):
172 172 """Make a valid python temp file."""
173 173 self.mktmp('pass\n')
174 174
175 175 def run_tmpfile(self):
176 176 _ip = get_ipython()
177 177 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
178 178 # See below and ticket https://bugs.launchpad.net/bugs/366353
179 179 _ip.magic('run %s' % self.fname)
180 180
181 181 def run_tmpfile_p(self):
182 182 _ip = get_ipython()
183 183 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
184 184 # See below and ticket https://bugs.launchpad.net/bugs/366353
185 185 _ip.magic('run -p %s' % self.fname)
186 186
187 187 def test_builtins_id(self):
188 188 """Check that %run doesn't damage __builtins__ """
189 189 _ip = get_ipython()
190 190 # Test that the id of __builtins__ is not modified by %run
191 191 bid1 = id(_ip.user_ns['__builtins__'])
192 192 self.run_tmpfile()
193 193 bid2 = id(_ip.user_ns['__builtins__'])
194 194 nt.assert_equal(bid1, bid2)
195 195
196 196 def test_builtins_type(self):
197 197 """Check that the type of __builtins__ doesn't change with %run.
198 198
199 199 However, the above could pass if __builtins__ was already modified to
200 200 be a dict (it should be a module) by a previous use of %run. So we
201 201 also check explicitly that it really is a module:
202 202 """
203 203 _ip = get_ipython()
204 204 self.run_tmpfile()
205 205 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
206 206
207 207 def test_run_profile( self ):
208 208 """Test that the option -p, which invokes the profiler, do not
209 209 crash by invoking execfile"""
210 get_ipython()
211 210 self.run_tmpfile_p()
212 211
212 def test_run_debug_twice(self):
213 # https://github.com/ipython/ipython/issues/10028
214 _ip = get_ipython()
215 with tt.fake_input(['c']):
216 _ip.magic('run -d %s' % self.fname)
217 with tt.fake_input(['c']):
218 _ip.magic('run -d %s' % self.fname)
219
213 220
214 221 class TestMagicRunSimple(tt.TempFileMixin):
215 222
216 223 def test_simpledef(self):
217 224 """Test that simple class definitions work."""
218 225 src = ("class foo: pass\n"
219 226 "def f(): return foo()")
220 227 self.mktmp(src)
221 228 _ip.magic('run %s' % self.fname)
222 229 _ip.run_cell('t = isinstance(f(), foo)')
223 230 nt.assert_true(_ip.user_ns['t'])
224 231
225 232 def test_obj_del(self):
226 233 """Test that object's __del__ methods are called on exit."""
227 234 if sys.platform == 'win32':
228 235 try:
229 236 import win32api
230 237 except ImportError:
231 238 raise SkipTest("Test requires pywin32")
232 239 src = ("class A(object):\n"
233 240 " def __del__(self):\n"
234 241 " print 'object A deleted'\n"
235 242 "a = A()\n")
236 243 self.mktmp(py3compat.doctest_refactor_print(src))
237 244 if dec.module_not_available('sqlite3'):
238 245 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
239 246 else:
240 247 err = None
241 248 tt.ipexec_validate(self.fname, 'object A deleted', err)
242 249
243 250 def test_aggressive_namespace_cleanup(self):
244 251 """Test that namespace cleanup is not too aggressive GH-238
245 252
246 253 Returning from another run magic deletes the namespace"""
247 254 # see ticket https://github.com/ipython/ipython/issues/238
248 255
249 256 with tt.TempFileMixin() as empty:
250 257 empty.mktmp('')
251 258 # On Windows, the filename will have \users in it, so we need to use the
252 259 # repr so that the \u becomes \\u.
253 260 src = ("ip = get_ipython()\n"
254 261 "for i in range(5):\n"
255 262 " try:\n"
256 263 " ip.magic(%r)\n"
257 264 " except NameError as e:\n"
258 265 " print(i)\n"
259 266 " break\n" % ('run ' + empty.fname))
260 267 self.mktmp(src)
261 268 _ip.magic('run %s' % self.fname)
262 269 _ip.run_cell('ip == get_ipython()')
263 270 nt.assert_equal(_ip.user_ns['i'], 4)
264 271
265 272 def test_run_second(self):
266 273 """Test that running a second file doesn't clobber the first, gh-3547
267 274 """
268 275 self.mktmp("avar = 1\n"
269 276 "def afunc():\n"
270 277 " return avar\n")
271 278
272 279 with tt.TempFileMixin() as empty:
273 280 empty.mktmp("")
274 281
275 282 _ip.magic('run %s' % self.fname)
276 283 _ip.magic('run %s' % empty.fname)
277 284 nt.assert_equal(_ip.user_ns['afunc'](), 1)
278 285
279 286 @dec.skip_win32
280 287 def test_tclass(self):
281 288 mydir = os.path.dirname(__file__)
282 289 tc = os.path.join(mydir, 'tclass')
283 290 src = ("%%run '%s' C-first\n"
284 291 "%%run '%s' C-second\n"
285 292 "%%run '%s' C-third\n") % (tc, tc, tc)
286 293 self.mktmp(src, '.ipy')
287 294 out = """\
288 295 ARGV 1-: ['C-first']
289 296 ARGV 1-: ['C-second']
290 297 tclass.py: deleting object: C-first
291 298 ARGV 1-: ['C-third']
292 299 tclass.py: deleting object: C-second
293 300 tclass.py: deleting object: C-third
294 301 """
295 302 if dec.module_not_available('sqlite3'):
296 303 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
297 304 else:
298 305 err = None
299 306 tt.ipexec_validate(self.fname, out, err)
300 307
301 308 def test_run_i_after_reset(self):
302 309 """Check that %run -i still works after %reset (gh-693)"""
303 310 src = "yy = zz\n"
304 311 self.mktmp(src)
305 312 _ip.run_cell("zz = 23")
306 313 _ip.magic('run -i %s' % self.fname)
307 314 nt.assert_equal(_ip.user_ns['yy'], 23)
308 315 _ip.magic('reset -f')
309 316 _ip.run_cell("zz = 23")
310 317 _ip.magic('run -i %s' % self.fname)
311 318 nt.assert_equal(_ip.user_ns['yy'], 23)
312 319
313 320 def test_unicode(self):
314 321 """Check that files in odd encodings are accepted."""
315 322 mydir = os.path.dirname(__file__)
316 323 na = os.path.join(mydir, 'nonascii.py')
317 324 _ip.magic('run "%s"' % na)
318 325 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
319 326
320 327 def test_run_py_file_attribute(self):
321 328 """Test handling of `__file__` attribute in `%run <file>.py`."""
322 329 src = "t = __file__\n"
323 330 self.mktmp(src)
324 331 _missing = object()
325 332 file1 = _ip.user_ns.get('__file__', _missing)
326 333 _ip.magic('run %s' % self.fname)
327 334 file2 = _ip.user_ns.get('__file__', _missing)
328 335
329 336 # Check that __file__ was equal to the filename in the script's
330 337 # namespace.
331 338 nt.assert_equal(_ip.user_ns['t'], self.fname)
332 339
333 340 # Check that __file__ was not leaked back into user_ns.
334 341 nt.assert_equal(file1, file2)
335 342
336 343 def test_run_ipy_file_attribute(self):
337 344 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
338 345 src = "t = __file__\n"
339 346 self.mktmp(src, ext='.ipy')
340 347 _missing = object()
341 348 file1 = _ip.user_ns.get('__file__', _missing)
342 349 _ip.magic('run %s' % self.fname)
343 350 file2 = _ip.user_ns.get('__file__', _missing)
344 351
345 352 # Check that __file__ was equal to the filename in the script's
346 353 # namespace.
347 354 nt.assert_equal(_ip.user_ns['t'], self.fname)
348 355
349 356 # Check that __file__ was not leaked back into user_ns.
350 357 nt.assert_equal(file1, file2)
351 358
352 359 def test_run_formatting(self):
353 360 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
354 361 src = "pass"
355 362 self.mktmp(src)
356 363 _ip.magic('run -t -N 1 %s' % self.fname)
357 364 _ip.magic('run -t -N 10 %s' % self.fname)
358 365
359 366 def test_ignore_sys_exit(self):
360 367 """Test the -e option to ignore sys.exit()"""
361 368 src = "import sys; sys.exit(1)"
362 369 self.mktmp(src)
363 370 with tt.AssertPrints('SystemExit'):
364 371 _ip.magic('run %s' % self.fname)
365 372
366 373 with tt.AssertNotPrints('SystemExit'):
367 374 _ip.magic('run -e %s' % self.fname)
368 375
369 376 def test_run_nb(self):
370 377 """Test %run notebook.ipynb"""
371 378 from nbformat import v4, writes
372 379 nb = v4.new_notebook(
373 380 cells=[
374 381 v4.new_markdown_cell("The Ultimate Question of Everything"),
375 382 v4.new_code_cell("answer=42")
376 383 ]
377 384 )
378 385 src = writes(nb, version=4)
379 386 self.mktmp(src, ext='.ipynb')
380 387
381 388 _ip.magic("run %s" % self.fname)
382 389
383 390 nt.assert_equal(_ip.user_ns['answer'], 42)
384 391
385 392
386 393
387 394 class TestMagicRunWithPackage(unittest.TestCase):
388 395
389 396 def writefile(self, name, content):
390 397 path = os.path.join(self.tempdir.name, name)
391 398 d = os.path.dirname(path)
392 399 if not os.path.isdir(d):
393 400 os.makedirs(d)
394 401 with open(path, 'w') as f:
395 402 f.write(textwrap.dedent(content))
396 403
397 404 def setUp(self):
398 405 self.package = package = 'tmp{0}'.format(repr(random.random())[2:])
399 406 """Temporary valid python package name."""
400 407
401 408 self.value = int(random.random() * 10000)
402 409
403 410 self.tempdir = TemporaryDirectory()
404 411 self.__orig_cwd = py3compat.getcwd()
405 412 sys.path.insert(0, self.tempdir.name)
406 413
407 414 self.writefile(os.path.join(package, '__init__.py'), '')
408 415 self.writefile(os.path.join(package, 'sub.py'), """
409 416 x = {0!r}
410 417 """.format(self.value))
411 418 self.writefile(os.path.join(package, 'relative.py'), """
412 419 from .sub import x
413 420 """)
414 421 self.writefile(os.path.join(package, 'absolute.py'), """
415 422 from {0}.sub import x
416 423 """.format(package))
417 424
418 425 def tearDown(self):
419 426 os.chdir(self.__orig_cwd)
420 427 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
421 428 self.tempdir.cleanup()
422 429
423 430 def check_run_submodule(self, submodule, opts=''):
424 431 _ip.user_ns.pop('x', None)
425 432 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
426 433 self.assertEqual(_ip.user_ns['x'], self.value,
427 434 'Variable `x` is not loaded from module `{0}`.'
428 435 .format(submodule))
429 436
430 437 def test_run_submodule_with_absolute_import(self):
431 438 self.check_run_submodule('absolute')
432 439
433 440 def test_run_submodule_with_relative_import(self):
434 441 """Run submodule that has a relative import statement (#2727)."""
435 442 self.check_run_submodule('relative')
436 443
437 444 def test_prun_submodule_with_absolute_import(self):
438 445 self.check_run_submodule('absolute', '-p')
439 446
440 447 def test_prun_submodule_with_relative_import(self):
441 448 self.check_run_submodule('relative', '-p')
442 449
443 450 def with_fake_debugger(func):
444 451 @functools.wraps(func)
445 452 def wrapper(*args, **kwds):
446 453 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
447 454 return func(*args, **kwds)
448 455 return wrapper
449 456
450 457 @with_fake_debugger
451 458 def test_debug_run_submodule_with_absolute_import(self):
452 459 self.check_run_submodule('absolute', '-d')
453 460
454 461 @with_fake_debugger
455 462 def test_debug_run_submodule_with_relative_import(self):
456 463 self.check_run_submodule('relative', '-d')
457 464
458 465 def test_run__name__():
459 466 with TemporaryDirectory() as td:
460 467 path = pjoin(td, 'foo.py')
461 468 with open(path, 'w') as f:
462 469 f.write("q = __name__")
463 470
464 471 _ip.user_ns.pop('q', None)
465 472 _ip.magic('run {}'.format(path))
466 473 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
467 474
468 475 _ip.magic('run -n {}'.format(path))
469 476 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
470 477
471 478 def test_run_tb():
472 479 """Test traceback offset in %run"""
473 480 with TemporaryDirectory() as td:
474 481 path = pjoin(td, 'foo.py')
475 482 with open(path, 'w') as f:
476 483 f.write('\n'.join([
477 484 "def foo():",
478 485 " return bar()",
479 486 "def bar():",
480 487 " raise RuntimeError('hello!')",
481 488 "foo()",
482 489 ]))
483 490 with capture_output() as io:
484 491 _ip.magic('run {}'.format(path))
485 492 out = io.stdout
486 493 nt.assert_not_in("execfile", out)
487 494 nt.assert_in("RuntimeError", out)
488 495 nt.assert_equal(out.count("---->"), 3)
489 496
490 497 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
491 498 def test_script_tb():
492 499 """Test traceback offset in `ipython script.py`"""
493 500 with TemporaryDirectory() as td:
494 501 path = pjoin(td, 'foo.py')
495 502 with open(path, 'w') as f:
496 503 f.write('\n'.join([
497 504 "def foo():",
498 505 " return bar()",
499 506 "def bar():",
500 507 " raise RuntimeError('hello!')",
501 508 "foo()",
502 509 ]))
503 510 out, err = tt.ipexec(path)
504 511 nt.assert_not_in("execfile", out)
505 512 nt.assert_in("RuntimeError", out)
506 513 nt.assert_equal(out.count("---->"), 3)
507 514
@@ -1,467 +1,476 b''
1 1 """Generic testing tools.
2 2
3 3 Authors
4 4 -------
5 5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 6 """
7 7
8 8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2009 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
19 11
20 12 import os
21 13 import re
22 14 import sys
23 15 import tempfile
24 16
25 17 from contextlib import contextmanager
26 18 from io import StringIO
27 19 from subprocess import Popen, PIPE
20 from unittest.mock import patch
28 21
29 22 try:
30 23 # These tools are used by parts of the runtime, so we make the nose
31 24 # dependency optional at this point. Nose is a hard dependency to run the
32 25 # test suite, but NOT to use ipython itself.
33 26 import nose.tools as nt
34 27 has_nose = True
35 28 except ImportError:
36 29 has_nose = False
37 30
38 31 from traitlets.config.loader import Config
39 32 from IPython.utils.process import get_output_error_code
40 33 from IPython.utils.text import list_strings
41 34 from IPython.utils.io import temp_pyfile, Tee
42 35 from IPython.utils import py3compat
43 36 from IPython.utils.encoding import DEFAULT_ENCODING
44 37
45 38 from . import decorators as dec
46 39 from . import skipdoctest
47 40
48 #-----------------------------------------------------------------------------
49 # Functions and classes
50 #-----------------------------------------------------------------------------
51 41
52 42 # The docstring for full_path doctests differently on win32 (different path
53 43 # separator) so just skip the doctest there. The example remains informative.
54 44 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
55 45
56 46 @doctest_deco
57 47 def full_path(startPath,files):
58 48 """Make full paths for all the listed files, based on startPath.
59 49
60 50 Only the base part of startPath is kept, since this routine is typically
61 51 used with a script's ``__file__`` variable as startPath. The base of startPath
62 52 is then prepended to all the listed files, forming the output list.
63 53
64 54 Parameters
65 55 ----------
66 56 startPath : string
67 57 Initial path to use as the base for the results. This path is split
68 58 using os.path.split() and only its first component is kept.
69 59
70 60 files : string or list
71 61 One or more files.
72 62
73 63 Examples
74 64 --------
75 65
76 66 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
77 67 ['/foo/a.txt', '/foo/b.txt']
78 68
79 69 >>> full_path('/foo',['a.txt','b.txt'])
80 70 ['/a.txt', '/b.txt']
81 71
82 72 If a single file is given, the output is still a list::
83 73
84 74 >>> full_path('/foo','a.txt')
85 75 ['/a.txt']
86 76 """
87 77
88 78 files = list_strings(files)
89 79 base = os.path.split(startPath)[0]
90 80 return [ os.path.join(base,f) for f in files ]
91 81
92 82
93 83 def parse_test_output(txt):
94 84 """Parse the output of a test run and return errors, failures.
95 85
96 86 Parameters
97 87 ----------
98 88 txt : str
99 89 Text output of a test run, assumed to contain a line of one of the
100 90 following forms::
101 91
102 92 'FAILED (errors=1)'
103 93 'FAILED (failures=1)'
104 94 'FAILED (errors=1, failures=1)'
105 95
106 96 Returns
107 97 -------
108 98 nerr, nfail
109 99 number of errors and failures.
110 100 """
111 101
112 102 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
113 103 if err_m:
114 104 nerr = int(err_m.group(1))
115 105 nfail = 0
116 106 return nerr, nfail
117 107
118 108 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
119 109 if fail_m:
120 110 nerr = 0
121 111 nfail = int(fail_m.group(1))
122 112 return nerr, nfail
123 113
124 114 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
125 115 re.MULTILINE)
126 116 if both_m:
127 117 nerr = int(both_m.group(1))
128 118 nfail = int(both_m.group(2))
129 119 return nerr, nfail
130 120
131 121 # If the input didn't match any of these forms, assume no error/failures
132 122 return 0, 0
133 123
134 124
135 125 # So nose doesn't think this is a test
136 126 parse_test_output.__test__ = False
137 127
138 128
139 129 def default_argv():
140 130 """Return a valid default argv for creating testing instances of ipython"""
141 131
142 132 return ['--quick', # so no config file is loaded
143 133 # Other defaults to minimize side effects on stdout
144 134 '--colors=NoColor', '--no-term-title','--no-banner',
145 135 '--autocall=0']
146 136
147 137
148 138 def default_config():
149 139 """Return a config object with good defaults for testing."""
150 140 config = Config()
151 141 config.TerminalInteractiveShell.colors = 'NoColor'
152 142 config.TerminalTerminalInteractiveShell.term_title = False,
153 143 config.TerminalInteractiveShell.autocall = 0
154 144 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
155 145 config.HistoryManager.hist_file = f.name
156 146 f.close()
157 147 config.HistoryManager.db_cache_size = 10000
158 148 return config
159 149
160 150
161 151 def get_ipython_cmd(as_string=False):
162 152 """
163 153 Return appropriate IPython command line name. By default, this will return
164 154 a list that can be used with subprocess.Popen, for example, but passing
165 155 `as_string=True` allows for returning the IPython command as a string.
166 156
167 157 Parameters
168 158 ----------
169 159 as_string: bool
170 160 Flag to allow to return the command as a string.
171 161 """
172 162 ipython_cmd = [sys.executable, "-m", "IPython"]
173 163
174 164 if as_string:
175 165 ipython_cmd = " ".join(ipython_cmd)
176 166
177 167 return ipython_cmd
178 168
179 169 def ipexec(fname, options=None, commands=()):
180 170 """Utility to call 'ipython filename'.
181 171
182 172 Starts IPython with a minimal and safe configuration to make startup as fast
183 173 as possible.
184 174
185 175 Note that this starts IPython in a subprocess!
186 176
187 177 Parameters
188 178 ----------
189 179 fname : str
190 180 Name of file to be executed (should have .py or .ipy extension).
191 181
192 182 options : optional, list
193 183 Extra command-line flags to be passed to IPython.
194 184
195 185 commands : optional, list
196 186 Commands to send in on stdin
197 187
198 188 Returns
199 189 -------
200 190 (stdout, stderr) of ipython subprocess.
201 191 """
202 192 if options is None: options = []
203 193
204 194 cmdargs = default_argv() + options
205 195
206 196 test_dir = os.path.dirname(__file__)
207 197
208 198 ipython_cmd = get_ipython_cmd()
209 199 # Absolute path for filename
210 200 full_fname = os.path.join(test_dir, fname)
211 201 full_cmd = ipython_cmd + cmdargs + [full_fname]
212 202 env = os.environ.copy()
213 203 # FIXME: ignore all warnings in ipexec while we have shims
214 204 # should we keep suppressing warnings here, even after removing shims?
215 205 env['PYTHONWARNINGS'] = 'ignore'
216 206 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
217 207 for k, v in env.items():
218 208 # Debug a bizarre failure we've seen on Windows:
219 209 # TypeError: environment can only contain strings
220 210 if not isinstance(v, str):
221 211 print(k, v)
222 212 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
223 213 out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None)
224 214 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
225 215 # `import readline` causes 'ESC[?1034h' to be output sometimes,
226 216 # so strip that out before doing comparisons
227 217 if out:
228 218 out = re.sub(r'\x1b\[[^h]+h', '', out)
229 219 return out, err
230 220
231 221
232 222 def ipexec_validate(fname, expected_out, expected_err='',
233 223 options=None, commands=()):
234 224 """Utility to call 'ipython filename' and validate output/error.
235 225
236 226 This function raises an AssertionError if the validation fails.
237 227
238 228 Note that this starts IPython in a subprocess!
239 229
240 230 Parameters
241 231 ----------
242 232 fname : str
243 233 Name of the file to be executed (should have .py or .ipy extension).
244 234
245 235 expected_out : str
246 236 Expected stdout of the process.
247 237
248 238 expected_err : optional, str
249 239 Expected stderr of the process.
250 240
251 241 options : optional, list
252 242 Extra command-line flags to be passed to IPython.
253 243
254 244 Returns
255 245 -------
256 246 None
257 247 """
258 248
259 249 import nose.tools as nt
260 250
261 251 out, err = ipexec(fname, options, commands)
262 252 #print 'OUT', out # dbg
263 253 #print 'ERR', err # dbg
264 254 # If there are any errors, we must check those befor stdout, as they may be
265 255 # more informative than simply having an empty stdout.
266 256 if err:
267 257 if expected_err:
268 258 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
269 259 else:
270 260 raise ValueError('Running file %r produced error: %r' %
271 261 (fname, err))
272 262 # If no errors or output on stderr was expected, match stdout
273 263 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
274 264
275 265
276 266 class TempFileMixin(object):
277 267 """Utility class to create temporary Python/IPython files.
278 268
279 269 Meant as a mixin class for test cases."""
280 270
281 271 def mktmp(self, src, ext='.py'):
282 272 """Make a valid python temp file."""
283 273 fname, f = temp_pyfile(src, ext)
284 274 self.tmpfile = f
285 275 self.fname = fname
286 276
287 277 def tearDown(self):
288 278 if hasattr(self, 'tmpfile'):
289 279 # If the tmpfile wasn't made because of skipped tests, like in
290 280 # win32, there's nothing to cleanup.
291 281 self.tmpfile.close()
292 282 try:
293 283 os.unlink(self.fname)
294 284 except:
295 285 # On Windows, even though we close the file, we still can't
296 286 # delete it. I have no clue why
297 287 pass
298 288
299 289 def __enter__(self):
300 290 return self
301 291
302 292 def __exit__(self, exc_type, exc_value, traceback):
303 293 self.tearDown()
304 294
305 295
306 296 pair_fail_msg = ("Testing {0}\n\n"
307 297 "In:\n"
308 298 " {1!r}\n"
309 299 "Expected:\n"
310 300 " {2!r}\n"
311 301 "Got:\n"
312 302 " {3!r}\n")
313 303 def check_pairs(func, pairs):
314 304 """Utility function for the common case of checking a function with a
315 305 sequence of input/output pairs.
316 306
317 307 Parameters
318 308 ----------
319 309 func : callable
320 310 The function to be tested. Should accept a single argument.
321 311 pairs : iterable
322 312 A list of (input, expected_output) tuples.
323 313
324 314 Returns
325 315 -------
326 316 None. Raises an AssertionError if any output does not match the expected
327 317 value.
328 318 """
329 319 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
330 320 for inp, expected in pairs:
331 321 out = func(inp)
332 322 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
333 323
334 324
335 325 if py3compat.PY3:
336 326 MyStringIO = StringIO
337 327 else:
338 328 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
339 329 # so we need a class that can handle both.
340 330 class MyStringIO(StringIO):
341 331 def write(self, s):
342 332 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
343 333 super(MyStringIO, self).write(s)
344 334
345 335 _re_type = type(re.compile(r''))
346 336
347 337 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
348 338 -------
349 339 {2!s}
350 340 -------
351 341 """
352 342
353 343 class AssertPrints(object):
354 344 """Context manager for testing that code prints certain text.
355 345
356 346 Examples
357 347 --------
358 348 >>> with AssertPrints("abc", suppress=False):
359 349 ... print("abcd")
360 350 ... print("def")
361 351 ...
362 352 abcd
363 353 def
364 354 """
365 355 def __init__(self, s, channel='stdout', suppress=True):
366 356 self.s = s
367 357 if isinstance(self.s, (py3compat.string_types, _re_type)):
368 358 self.s = [self.s]
369 359 self.channel = channel
370 360 self.suppress = suppress
371 361
372 362 def __enter__(self):
373 363 self.orig_stream = getattr(sys, self.channel)
374 364 self.buffer = MyStringIO()
375 365 self.tee = Tee(self.buffer, channel=self.channel)
376 366 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
377 367
378 368 def __exit__(self, etype, value, traceback):
379 369 try:
380 370 if value is not None:
381 371 # If an error was raised, don't check anything else
382 372 return False
383 373 self.tee.flush()
384 374 setattr(sys, self.channel, self.orig_stream)
385 375 printed = self.buffer.getvalue()
386 376 for s in self.s:
387 377 if isinstance(s, _re_type):
388 378 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
389 379 else:
390 380 assert s in printed, notprinted_msg.format(s, self.channel, printed)
391 381 return False
392 382 finally:
393 383 self.tee.close()
394 384
395 385 printed_msg = """Found {0!r} in printed output (on {1}):
396 386 -------
397 387 {2!s}
398 388 -------
399 389 """
400 390
401 391 class AssertNotPrints(AssertPrints):
402 392 """Context manager for checking that certain output *isn't* produced.
403 393
404 394 Counterpart of AssertPrints"""
405 395 def __exit__(self, etype, value, traceback):
406 396 try:
407 397 if value is not None:
408 398 # If an error was raised, don't check anything else
409 399 self.tee.close()
410 400 return False
411 401 self.tee.flush()
412 402 setattr(sys, self.channel, self.orig_stream)
413 403 printed = self.buffer.getvalue()
414 404 for s in self.s:
415 405 if isinstance(s, _re_type):
416 406 assert not s.search(printed),printed_msg.format(
417 407 s.pattern, self.channel, printed)
418 408 else:
419 409 assert s not in printed, printed_msg.format(
420 410 s, self.channel, printed)
421 411 return False
422 412 finally:
423 413 self.tee.close()
424 414
425 415 @contextmanager
426 416 def mute_warn():
427 417 from IPython.utils import warn
428 418 save_warn = warn.warn
429 419 warn.warn = lambda *a, **kw: None
430 420 try:
431 421 yield
432 422 finally:
433 423 warn.warn = save_warn
434 424
435 425 @contextmanager
436 426 def make_tempfile(name):
437 427 """ Create an empty, named, temporary file for the duration of the context.
438 428 """
439 429 f = open(name, 'w')
440 430 f.close()
441 431 try:
442 432 yield
443 433 finally:
444 434 os.unlink(name)
445 435
436 def fake_input(inputs):
437 """Temporarily replace the input() function to return the given values
438
439 Use as a context manager:
440
441 with fake_input(['result1', 'result2']):
442 ...
443
444 Values are returned in order. If input() is called again after the last value
445 was used, EOFError is raised.
446 """
447 it = iter(inputs)
448 def mock_input(prompt=''):
449 try:
450 return next(it)
451 except StopIteration:
452 raise EOFError('No more inputs given')
453
454 return patch('builtins.input', mock_input)
446 455
447 456 def help_output_test(subcommand=''):
448 457 """test that `ipython [subcommand] -h` works"""
449 458 cmd = get_ipython_cmd() + [subcommand, '-h']
450 459 out, err, rc = get_output_error_code(cmd)
451 460 nt.assert_equal(rc, 0, err)
452 461 nt.assert_not_in("Traceback", err)
453 462 nt.assert_in("Options", out)
454 463 nt.assert_in("--help-all", out)
455 464 return out, err
456 465
457 466
458 467 def help_all_output_test(subcommand=''):
459 468 """test that `ipython [subcommand] --help-all` works"""
460 469 cmd = get_ipython_cmd() + [subcommand, '--help-all']
461 470 out, err, rc = get_output_error_code(cmd)
462 471 nt.assert_equal(rc, 0, err)
463 472 nt.assert_not_in("Traceback", err)
464 473 nt.assert_in("Options", out)
465 474 nt.assert_in("Class parameters", out)
466 475 return out, err
467 476
General Comments 0
You need to be logged in to leave comments. Login now