##// END OF EJS Templates
Fix Mixin Inheritence in Xunit unittest....
Matthias Bussonnier -
Show More
@@ -1,993 +1,1004 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import asyncio
13 13 import ast
14 14 import os
15 15 import signal
16 16 import shutil
17 17 import sys
18 18 import tempfile
19 19 import unittest
20 20 from unittest import mock
21 21
22 22 from os.path import join
23 23
24 24 import nose.tools as nt
25 25
26 26 from IPython.core.error import InputRejected
27 27 from IPython.core.inputtransformer import InputTransformer
28 28 from IPython.core import interactiveshell
29 29 from IPython.testing.decorators import (
30 30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 31 )
32 32 from IPython.testing import tools as tt
33 33 from IPython.utils.process import find_cmd
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Globals
37 37 #-----------------------------------------------------------------------------
38 38 # This is used by every single test, no point repeating it ad nauseam
39 39 ip = get_ipython()
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Tests
43 43 #-----------------------------------------------------------------------------
44 44
45 45 class DerivedInterrupt(KeyboardInterrupt):
46 46 pass
47 47
48 48 class InteractiveShellTestCase(unittest.TestCase):
49 49 def test_naked_string_cells(self):
50 50 """Test that cells with only naked strings are fully executed"""
51 51 # First, single-line inputs
52 52 ip.run_cell('"a"\n')
53 53 self.assertEqual(ip.user_ns['_'], 'a')
54 54 # And also multi-line cells
55 55 ip.run_cell('"""a\nb"""\n')
56 56 self.assertEqual(ip.user_ns['_'], 'a\nb')
57 57
58 58 def test_run_empty_cell(self):
59 59 """Just make sure we don't get a horrible error with a blank
60 60 cell of input. Yes, I did overlook that."""
61 61 old_xc = ip.execution_count
62 62 res = ip.run_cell('')
63 63 self.assertEqual(ip.execution_count, old_xc)
64 64 self.assertEqual(res.execution_count, None)
65 65
66 66 def test_run_cell_multiline(self):
67 67 """Multi-block, multi-line cells must execute correctly.
68 68 """
69 69 src = '\n'.join(["x=1",
70 70 "y=2",
71 71 "if 1:",
72 72 " x += 1",
73 73 " y += 1",])
74 74 res = ip.run_cell(src)
75 75 self.assertEqual(ip.user_ns['x'], 2)
76 76 self.assertEqual(ip.user_ns['y'], 3)
77 77 self.assertEqual(res.success, True)
78 78 self.assertEqual(res.result, None)
79 79
80 80 def test_multiline_string_cells(self):
81 81 "Code sprinkled with multiline strings should execute (GH-306)"
82 82 ip.run_cell('tmp=0')
83 83 self.assertEqual(ip.user_ns['tmp'], 0)
84 84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 85 self.assertEqual(ip.user_ns['tmp'], 1)
86 86 self.assertEqual(res.success, True)
87 87 self.assertEqual(res.result, "a\nb")
88 88
89 89 def test_dont_cache_with_semicolon(self):
90 90 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 91 oldlen = len(ip.user_ns['Out'])
92 92 for cell in ['1;', '1;1;']:
93 93 res = ip.run_cell(cell, store_history=True)
94 94 newlen = len(ip.user_ns['Out'])
95 95 self.assertEqual(oldlen, newlen)
96 96 self.assertIsNone(res.result)
97 97 i = 0
98 98 #also test the default caching behavior
99 99 for cell in ['1', '1;1']:
100 100 ip.run_cell(cell, store_history=True)
101 101 newlen = len(ip.user_ns['Out'])
102 102 i += 1
103 103 self.assertEqual(oldlen+i, newlen)
104 104
105 105 def test_syntax_error(self):
106 106 res = ip.run_cell("raise = 3")
107 107 self.assertIsInstance(res.error_before_exec, SyntaxError)
108 108
109 109 def test_In_variable(self):
110 110 "Verify that In variable grows with user input (GH-284)"
111 111 oldlen = len(ip.user_ns['In'])
112 112 ip.run_cell('1;', store_history=True)
113 113 newlen = len(ip.user_ns['In'])
114 114 self.assertEqual(oldlen+1, newlen)
115 115 self.assertEqual(ip.user_ns['In'][-1],'1;')
116 116
117 117 def test_magic_names_in_string(self):
118 118 ip.run_cell('a = """\n%exit\n"""')
119 119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120 120
121 121 def test_trailing_newline(self):
122 122 """test that running !(command) does not raise a SyntaxError"""
123 123 ip.run_cell('!(true)\n', False)
124 124 ip.run_cell('!(true)\n\n\n', False)
125 125
126 126 def test_gh_597(self):
127 127 """Pretty-printing lists of objects with non-ascii reprs may cause
128 128 problems."""
129 129 class Spam(object):
130 130 def __repr__(self):
131 131 return "\xe9"*50
132 132 import IPython.core.formatters
133 133 f = IPython.core.formatters.PlainTextFormatter()
134 134 f([Spam(),Spam()])
135 135
136 136
137 137 def test_future_flags(self):
138 138 """Check that future flags are used for parsing code (gh-777)"""
139 139 ip.run_cell('from __future__ import barry_as_FLUFL')
140 140 try:
141 141 ip.run_cell('prfunc_return_val = 1 <> 2')
142 142 assert 'prfunc_return_val' in ip.user_ns
143 143 finally:
144 144 # Reset compiler flags so we don't mess up other tests.
145 145 ip.compile.reset_compiler_flags()
146 146
147 147 def test_can_pickle(self):
148 148 "Can we pickle objects defined interactively (GH-29)"
149 149 ip = get_ipython()
150 150 ip.reset()
151 151 ip.run_cell(("class Mylist(list):\n"
152 152 " def __init__(self,x=[]):\n"
153 153 " list.__init__(self,x)"))
154 154 ip.run_cell("w=Mylist([1,2,3])")
155 155
156 156 from pickle import dumps
157 157
158 158 # We need to swap in our main module - this is only necessary
159 159 # inside the test framework, because IPython puts the interactive module
160 160 # in place (but the test framework undoes this).
161 161 _main = sys.modules['__main__']
162 162 sys.modules['__main__'] = ip.user_module
163 163 try:
164 164 res = dumps(ip.user_ns["w"])
165 165 finally:
166 166 sys.modules['__main__'] = _main
167 167 self.assertTrue(isinstance(res, bytes))
168 168
169 169 def test_global_ns(self):
170 170 "Code in functions must be able to access variables outside them."
171 171 ip = get_ipython()
172 172 ip.run_cell("a = 10")
173 173 ip.run_cell(("def f(x):\n"
174 174 " return x + a"))
175 175 ip.run_cell("b = f(12)")
176 176 self.assertEqual(ip.user_ns["b"], 22)
177 177
178 178 def test_bad_custom_tb(self):
179 179 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 181 self.assertEqual(ip.custom_exceptions, (IOError,))
182 182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 183 ip.run_cell(u'raise IOError("foo")')
184 184 self.assertEqual(ip.custom_exceptions, ())
185 185
186 186 def test_bad_custom_tb_return(self):
187 187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 189 self.assertEqual(ip.custom_exceptions, (NameError,))
190 190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 191 ip.run_cell(u'a=abracadabra')
192 192 self.assertEqual(ip.custom_exceptions, ())
193 193
194 194 def test_drop_by_id(self):
195 195 myvars = {"a":object(), "b":object(), "c": object()}
196 196 ip.push(myvars, interactive=False)
197 197 for name in myvars:
198 198 assert name in ip.user_ns, name
199 199 assert name in ip.user_ns_hidden, name
200 200 ip.user_ns['b'] = 12
201 201 ip.drop_by_id(myvars)
202 202 for name in ["a", "c"]:
203 203 assert name not in ip.user_ns, name
204 204 assert name not in ip.user_ns_hidden, name
205 205 assert ip.user_ns['b'] == 12
206 206 ip.reset()
207 207
208 208 def test_var_expand(self):
209 209 ip.user_ns['f'] = u'Ca\xf1o'
210 210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
211 211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214 214
215 215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216 216
217 217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 218 # This should not raise any exception:
219 219 ip.var_expand(u'echo $f')
220 220
221 221 def test_var_expand_local(self):
222 222 """Test local variable expansion in !system and %magic calls"""
223 223 # !system
224 224 ip.run_cell('def test():\n'
225 225 ' lvar = "ttt"\n'
226 226 ' ret = !echo {lvar}\n'
227 227 ' return ret[0]\n')
228 228 res = ip.user_ns['test']()
229 229 nt.assert_in('ttt', res)
230 230
231 231 # %magic
232 232 ip.run_cell('def makemacro():\n'
233 233 ' macroname = "macro_var_expand_locals"\n'
234 234 ' %macro {macroname} codestr\n')
235 235 ip.user_ns['codestr'] = "str(12)"
236 236 ip.run_cell('makemacro()')
237 237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238 238
239 239 def test_var_expand_self(self):
240 240 """Test variable expansion with the name 'self', which was failing.
241 241
242 242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 243 """
244 244 ip.run_cell('class cTest:\n'
245 245 ' classvar="see me"\n'
246 246 ' def test(self):\n'
247 247 ' res = !echo Variable: {self.classvar}\n'
248 248 ' return res[0]\n')
249 249 nt.assert_in('see me', ip.user_ns['cTest']().test())
250 250
251 251 def test_bad_var_expand(self):
252 252 """var_expand on invalid formats shouldn't raise"""
253 253 # SyntaxError
254 254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 255 # NameError
256 256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 257 # ZeroDivisionError
258 258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259 259
260 260 def test_silent_postexec(self):
261 261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 262 pre_explicit = mock.Mock()
263 263 pre_always = mock.Mock()
264 264 post_explicit = mock.Mock()
265 265 post_always = mock.Mock()
266 266 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
267 267
268 268 ip.events.register('pre_run_cell', pre_explicit)
269 269 ip.events.register('pre_execute', pre_always)
270 270 ip.events.register('post_run_cell', post_explicit)
271 271 ip.events.register('post_execute', post_always)
272 272
273 273 try:
274 274 ip.run_cell("1", silent=True)
275 275 assert pre_always.called
276 276 assert not pre_explicit.called
277 277 assert post_always.called
278 278 assert not post_explicit.called
279 279 # double-check that non-silent exec did what we expected
280 280 # silent to avoid
281 281 ip.run_cell("1")
282 282 assert pre_explicit.called
283 283 assert post_explicit.called
284 284 info, = pre_explicit.call_args[0]
285 285 result, = post_explicit.call_args[0]
286 286 self.assertEqual(info, result.info)
287 287 # check that post hooks are always called
288 288 [m.reset_mock() for m in all_mocks]
289 289 ip.run_cell("syntax error")
290 290 assert pre_always.called
291 291 assert pre_explicit.called
292 292 assert post_always.called
293 293 assert post_explicit.called
294 294 info, = pre_explicit.call_args[0]
295 295 result, = post_explicit.call_args[0]
296 296 self.assertEqual(info, result.info)
297 297 finally:
298 298 # remove post-exec
299 299 ip.events.unregister('pre_run_cell', pre_explicit)
300 300 ip.events.unregister('pre_execute', pre_always)
301 301 ip.events.unregister('post_run_cell', post_explicit)
302 302 ip.events.unregister('post_execute', post_always)
303 303
304 304 def test_silent_noadvance(self):
305 305 """run_cell(silent=True) doesn't advance execution_count"""
306 306 ec = ip.execution_count
307 307 # silent should force store_history=False
308 308 ip.run_cell("1", store_history=True, silent=True)
309 309
310 310 self.assertEqual(ec, ip.execution_count)
311 311 # double-check that non-silent exec did what we expected
312 312 # silent to avoid
313 313 ip.run_cell("1", store_history=True)
314 314 self.assertEqual(ec+1, ip.execution_count)
315 315
316 316 def test_silent_nodisplayhook(self):
317 317 """run_cell(silent=True) doesn't trigger displayhook"""
318 318 d = dict(called=False)
319 319
320 320 trap = ip.display_trap
321 321 save_hook = trap.hook
322 322
323 323 def failing_hook(*args, **kwargs):
324 324 d['called'] = True
325 325
326 326 try:
327 327 trap.hook = failing_hook
328 328 res = ip.run_cell("1", silent=True)
329 329 self.assertFalse(d['called'])
330 330 self.assertIsNone(res.result)
331 331 # double-check that non-silent exec did what we expected
332 332 # silent to avoid
333 333 ip.run_cell("1")
334 334 self.assertTrue(d['called'])
335 335 finally:
336 336 trap.hook = save_hook
337 337
338 338 def test_ofind_line_magic(self):
339 339 from IPython.core.magic import register_line_magic
340 340
341 341 @register_line_magic
342 342 def lmagic(line):
343 343 "A line magic"
344 344
345 345 # Get info on line magic
346 346 lfind = ip._ofind('lmagic')
347 347 info = dict(found=True, isalias=False, ismagic=True,
348 348 namespace = 'IPython internal', obj= lmagic.__wrapped__,
349 349 parent = None)
350 350 nt.assert_equal(lfind, info)
351 351
352 352 def test_ofind_cell_magic(self):
353 353 from IPython.core.magic import register_cell_magic
354 354
355 355 @register_cell_magic
356 356 def cmagic(line, cell):
357 357 "A cell magic"
358 358
359 359 # Get info on cell magic
360 360 find = ip._ofind('cmagic')
361 361 info = dict(found=True, isalias=False, ismagic=True,
362 362 namespace = 'IPython internal', obj= cmagic.__wrapped__,
363 363 parent = None)
364 364 nt.assert_equal(find, info)
365 365
366 366 def test_ofind_property_with_error(self):
367 367 class A(object):
368 368 @property
369 369 def foo(self):
370 370 raise NotImplementedError()
371 371 a = A()
372 372
373 373 found = ip._ofind('a.foo', [('locals', locals())])
374 374 info = dict(found=True, isalias=False, ismagic=False,
375 375 namespace='locals', obj=A.foo, parent=a)
376 376 nt.assert_equal(found, info)
377 377
378 378 def test_ofind_multiple_attribute_lookups(self):
379 379 class A(object):
380 380 @property
381 381 def foo(self):
382 382 raise NotImplementedError()
383 383
384 384 a = A()
385 385 a.a = A()
386 386 a.a.a = A()
387 387
388 388 found = ip._ofind('a.a.a.foo', [('locals', locals())])
389 389 info = dict(found=True, isalias=False, ismagic=False,
390 390 namespace='locals', obj=A.foo, parent=a.a.a)
391 391 nt.assert_equal(found, info)
392 392
393 393 def test_ofind_slotted_attributes(self):
394 394 class A(object):
395 395 __slots__ = ['foo']
396 396 def __init__(self):
397 397 self.foo = 'bar'
398 398
399 399 a = A()
400 400 found = ip._ofind('a.foo', [('locals', locals())])
401 401 info = dict(found=True, isalias=False, ismagic=False,
402 402 namespace='locals', obj=a.foo, parent=a)
403 403 nt.assert_equal(found, info)
404 404
405 405 found = ip._ofind('a.bar', [('locals', locals())])
406 406 info = dict(found=False, isalias=False, ismagic=False,
407 407 namespace=None, obj=None, parent=a)
408 408 nt.assert_equal(found, info)
409 409
410 410 def test_ofind_prefers_property_to_instance_level_attribute(self):
411 411 class A(object):
412 412 @property
413 413 def foo(self):
414 414 return 'bar'
415 415 a = A()
416 416 a.__dict__['foo'] = 'baz'
417 417 nt.assert_equal(a.foo, 'bar')
418 418 found = ip._ofind('a.foo', [('locals', locals())])
419 419 nt.assert_is(found['obj'], A.foo)
420 420
421 421 def test_custom_syntaxerror_exception(self):
422 422 called = []
423 423 def my_handler(shell, etype, value, tb, tb_offset=None):
424 424 called.append(etype)
425 425 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
426 426
427 427 ip.set_custom_exc((SyntaxError,), my_handler)
428 428 try:
429 429 ip.run_cell("1f")
430 430 # Check that this was called, and only once.
431 431 self.assertEqual(called, [SyntaxError])
432 432 finally:
433 433 # Reset the custom exception hook
434 434 ip.set_custom_exc((), None)
435 435
436 436 def test_custom_exception(self):
437 437 called = []
438 438 def my_handler(shell, etype, value, tb, tb_offset=None):
439 439 called.append(etype)
440 440 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
441 441
442 442 ip.set_custom_exc((ValueError,), my_handler)
443 443 try:
444 444 res = ip.run_cell("raise ValueError('test')")
445 445 # Check that this was called, and only once.
446 446 self.assertEqual(called, [ValueError])
447 447 # Check that the error is on the result object
448 448 self.assertIsInstance(res.error_in_exec, ValueError)
449 449 finally:
450 450 # Reset the custom exception hook
451 451 ip.set_custom_exc((), None)
452 452
453 453 def test_mktempfile(self):
454 454 filename = ip.mktempfile()
455 455 # Check that we can open the file again on Windows
456 456 with open(filename, 'w') as f:
457 457 f.write('abc')
458 458
459 459 filename = ip.mktempfile(data='blah')
460 460 with open(filename, 'r') as f:
461 461 self.assertEqual(f.read(), 'blah')
462 462
463 463 def test_new_main_mod(self):
464 464 # Smoketest to check that this accepts a unicode module name
465 465 name = u'jiefmw'
466 466 mod = ip.new_main_mod(u'%s.py' % name, name)
467 467 self.assertEqual(mod.__name__, name)
468 468
469 469 def test_get_exception_only(self):
470 470 try:
471 471 raise KeyboardInterrupt
472 472 except KeyboardInterrupt:
473 473 msg = ip.get_exception_only()
474 474 self.assertEqual(msg, 'KeyboardInterrupt\n')
475 475
476 476 try:
477 477 raise DerivedInterrupt("foo")
478 478 except KeyboardInterrupt:
479 479 msg = ip.get_exception_only()
480 480 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
481 481
482 482 def test_inspect_text(self):
483 483 ip.run_cell('a = 5')
484 484 text = ip.object_inspect_text('a')
485 485 self.assertIsInstance(text, str)
486 486
487 487 def test_last_execution_result(self):
488 488 """ Check that last execution result gets set correctly (GH-10702) """
489 489 result = ip.run_cell('a = 5; a')
490 490 self.assertTrue(ip.last_execution_succeeded)
491 491 self.assertEqual(ip.last_execution_result.result, 5)
492 492
493 493 result = ip.run_cell('a = x_invalid_id_x')
494 494 self.assertFalse(ip.last_execution_succeeded)
495 495 self.assertFalse(ip.last_execution_result.success)
496 496 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
497 497
498 498 def test_reset_aliasing(self):
499 499 """ Check that standard posix aliases work after %reset. """
500 500 if os.name != 'posix':
501 501 return
502 502
503 503 ip.reset()
504 504 for cmd in ('clear', 'more', 'less', 'man'):
505 505 res = ip.run_cell('%' + cmd)
506 506 self.assertEqual(res.success, True)
507 507
508 508
509 509 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
510 510
511 511 @onlyif_unicode_paths
512 512 def setUp(self):
513 513 self.BASETESTDIR = tempfile.mkdtemp()
514 514 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
515 515 os.mkdir(self.TESTDIR)
516 516 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
517 517 sfile.write("pass\n")
518 518 self.oldpath = os.getcwd()
519 519 os.chdir(self.TESTDIR)
520 520 self.fname = u"Γ₯Àâtestscript.py"
521 521
522 522 def tearDown(self):
523 523 os.chdir(self.oldpath)
524 524 shutil.rmtree(self.BASETESTDIR)
525 525
526 526 @onlyif_unicode_paths
527 527 def test_1(self):
528 528 """Test safe_execfile with non-ascii path
529 529 """
530 530 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
531 531
532 532 class ExitCodeChecks(tt.TempFileMixin):
533
534 def setUp(self):
535 self.system = ip.system_raw
536
533 537 def test_exit_code_ok(self):
534 538 self.system('exit 0')
535 539 self.assertEqual(ip.user_ns['_exit_code'], 0)
536 540
537 541 def test_exit_code_error(self):
538 542 self.system('exit 1')
539 543 self.assertEqual(ip.user_ns['_exit_code'], 1)
540 544
541 545 @skipif(not hasattr(signal, 'SIGALRM'))
542 546 def test_exit_code_signal(self):
543 547 self.mktmp("import signal, time\n"
544 548 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
545 549 "time.sleep(1)\n")
546 550 self.system("%s %s" % (sys.executable, self.fname))
547 551 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
548 552
549 553 @onlyif_cmds_exist("csh")
550 554 def test_exit_code_signal_csh(self):
551 555 SHELL = os.environ.get('SHELL', None)
552 556 os.environ['SHELL'] = find_cmd("csh")
553 557 try:
554 558 self.test_exit_code_signal()
555 559 finally:
556 560 if SHELL is not None:
557 561 os.environ['SHELL'] = SHELL
558 562 else:
559 563 del os.environ['SHELL']
560 564
561 565
562 class TestSystemRaw(ExitCodeChecks, unittest.TestCase):
563 system = ip.system_raw
566 class TestSystemRaw(ExitCodeChecks):
567
568 def setUp(self):
569 super().setUp()
570 self.sytem = ip.system_raw
564 571
565 572 @onlyif_unicode_paths
566 573 def test_1(self):
567 574 """Test system_raw with non-ascii cmd
568 575 """
569 576 cmd = u'''python -c "'Γ₯Àâ'" '''
570 577 ip.system_raw(cmd)
571 578
572 579 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
573 580 @mock.patch('os.system', side_effect=KeyboardInterrupt)
574 581 def test_control_c(self, *mocks):
575 582 try:
576 583 self.system("sleep 1 # wont happen")
577 584 except KeyboardInterrupt:
578 585 self.fail("system call should intercept "
579 586 "keyboard interrupt from subprocess.call")
580 587 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
581 588
582 589 # TODO: Exit codes are currently ignored on Windows.
583 class TestSystemPipedExitCode(ExitCodeChecks, unittest.TestCase):
584 system = ip.system_piped
590 class TestSystemPipedExitCode(ExitCodeChecks):
591
592 def setUp(self):
593 super().setUp()
594 self.sytem = ip.system_piped
585 595
586 596 @skip_win32
587 597 def test_exit_code_ok(self):
588 598 ExitCodeChecks.test_exit_code_ok(self)
589 599
590 600 @skip_win32
591 601 def test_exit_code_error(self):
592 602 ExitCodeChecks.test_exit_code_error(self)
593 603
594 604 @skip_win32
595 605 def test_exit_code_signal(self):
596 606 ExitCodeChecks.test_exit_code_signal(self)
597 607
598 class TestModules(tt.TempFileMixin, unittest.TestCase):
608 class TestModules(tt.TempFileMixin):
599 609 def test_extraneous_loads(self):
600 610 """Test we're not loading modules on startup that we shouldn't.
601 611 """
602 612 self.mktmp("import sys\n"
603 613 "print('numpy' in sys.modules)\n"
604 614 "print('ipyparallel' in sys.modules)\n"
605 615 "print('ipykernel' in sys.modules)\n"
606 616 )
607 617 out = "False\nFalse\nFalse\n"
608 618 tt.ipexec_validate(self.fname, out)
609 619
610 620 class Negator(ast.NodeTransformer):
611 621 """Negates all number literals in an AST."""
612 622
613 623 # for python 3.7 and earlier
614 624 def visit_Num(self, node):
615 625 node.n = -node.n
616 626 return node
617 627
618 628 # for python 3.8+
619 629 def visit_Constant(self, node):
620 630 if isinstance(node.value, int):
621 631 return self.visit_Num(node)
622 632 return node
623 633
624 634 class TestAstTransform(unittest.TestCase):
625 635 def setUp(self):
626 636 self.negator = Negator()
627 637 ip.ast_transformers.append(self.negator)
628 638
629 639 def tearDown(self):
630 640 ip.ast_transformers.remove(self.negator)
631 641
632 642 def test_run_cell(self):
633 643 with tt.AssertPrints('-34'):
634 644 ip.run_cell('print (12 + 22)')
635 645
636 646 # A named reference to a number shouldn't be transformed.
637 647 ip.user_ns['n'] = 55
638 648 with tt.AssertNotPrints('-55'):
639 649 ip.run_cell('print (n)')
640 650
641 651 def test_timeit(self):
642 652 called = set()
643 653 def f(x):
644 654 called.add(x)
645 655 ip.push({'f':f})
646 656
647 657 with tt.AssertPrints("std. dev. of"):
648 658 ip.run_line_magic("timeit", "-n1 f(1)")
649 659 self.assertEqual(called, {-1})
650 660 called.clear()
651 661
652 662 with tt.AssertPrints("std. dev. of"):
653 663 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
654 664 self.assertEqual(called, {-2, -3})
655 665
656 666 def test_time(self):
657 667 called = []
658 668 def f(x):
659 669 called.append(x)
660 670 ip.push({'f':f})
661 671
662 672 # Test with an expression
663 673 with tt.AssertPrints("Wall time: "):
664 674 ip.run_line_magic("time", "f(5+9)")
665 675 self.assertEqual(called, [-14])
666 676 called[:] = []
667 677
668 678 # Test with a statement (different code path)
669 679 with tt.AssertPrints("Wall time: "):
670 680 ip.run_line_magic("time", "a = f(-3 + -2)")
671 681 self.assertEqual(called, [5])
672 682
673 683 def test_macro(self):
674 684 ip.push({'a':10})
675 685 # The AST transformation makes this do a+=-1
676 686 ip.define_macro("amacro", "a+=1\nprint(a)")
677 687
678 688 with tt.AssertPrints("9"):
679 689 ip.run_cell("amacro")
680 690 with tt.AssertPrints("8"):
681 691 ip.run_cell("amacro")
682 692
683 693 class IntegerWrapper(ast.NodeTransformer):
684 694 """Wraps all integers in a call to Integer()"""
685 695
686 696 # for Python 3.7 and earlier
687 697
688 698 # for Python 3.7 and earlier
689 699 def visit_Num(self, node):
690 700 if isinstance(node.n, int):
691 701 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
692 702 args=[node], keywords=[])
693 703 return node
694 704
695 705 # For Python 3.8+
696 706 def visit_Constant(self, node):
697 707 if isinstance(node.value, int):
698 708 return self.visit_Num(node)
699 709 return node
700 710
701 711
702 712 class TestAstTransform2(unittest.TestCase):
703 713 def setUp(self):
704 714 self.intwrapper = IntegerWrapper()
705 715 ip.ast_transformers.append(self.intwrapper)
706 716
707 717 self.calls = []
708 718 def Integer(*args):
709 719 self.calls.append(args)
710 720 return args
711 721 ip.push({"Integer": Integer})
712 722
713 723 def tearDown(self):
714 724 ip.ast_transformers.remove(self.intwrapper)
715 725 del ip.user_ns['Integer']
716 726
717 727 def test_run_cell(self):
718 728 ip.run_cell("n = 2")
719 729 self.assertEqual(self.calls, [(2,)])
720 730
721 731 # This shouldn't throw an error
722 732 ip.run_cell("o = 2.0")
723 733 self.assertEqual(ip.user_ns['o'], 2.0)
724 734
725 735 def test_timeit(self):
726 736 called = set()
727 737 def f(x):
728 738 called.add(x)
729 739 ip.push({'f':f})
730 740
731 741 with tt.AssertPrints("std. dev. of"):
732 742 ip.run_line_magic("timeit", "-n1 f(1)")
733 743 self.assertEqual(called, {(1,)})
734 744 called.clear()
735 745
736 746 with tt.AssertPrints("std. dev. of"):
737 747 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
738 748 self.assertEqual(called, {(2,), (3,)})
739 749
740 750 class ErrorTransformer(ast.NodeTransformer):
741 751 """Throws an error when it sees a number."""
742 752
743 753 # for Python 3.7 and earlier
744 754 def visit_Num(self, node):
745 755 raise ValueError("test")
746 756
747 757 # for Python 3.8+
748 758 def visit_Constant(self, node):
749 759 if isinstance(node.value, int):
750 760 return self.visit_Num(node)
751 761 return node
752 762
753 763
754 764 class TestAstTransformError(unittest.TestCase):
755 765 def test_unregistering(self):
756 766 err_transformer = ErrorTransformer()
757 767 ip.ast_transformers.append(err_transformer)
758 768
759 769 with tt.AssertPrints("unregister", channel='stderr'):
760 770 ip.run_cell("1 + 2")
761 771
762 772 # This should have been removed.
763 773 nt.assert_not_in(err_transformer, ip.ast_transformers)
764 774
765 775
766 776 class StringRejector(ast.NodeTransformer):
767 777 """Throws an InputRejected when it sees a string literal.
768 778
769 779 Used to verify that NodeTransformers can signal that a piece of code should
770 780 not be executed by throwing an InputRejected.
771 781 """
772 782
773 783 #for python 3.7 and earlier
774 784 def visit_Str(self, node):
775 785 raise InputRejected("test")
776 786
777 787 # 3.8 only
778 788 def visit_Constant(self, node):
779 789 if isinstance(node.value, str):
780 790 raise InputRejected("test")
781 791 return node
782 792
783 793
784 794 class TestAstTransformInputRejection(unittest.TestCase):
785 795
786 796 def setUp(self):
787 797 self.transformer = StringRejector()
788 798 ip.ast_transformers.append(self.transformer)
789 799
790 800 def tearDown(self):
791 801 ip.ast_transformers.remove(self.transformer)
792 802
793 803 def test_input_rejection(self):
794 804 """Check that NodeTransformers can reject input."""
795 805
796 806 expect_exception_tb = tt.AssertPrints("InputRejected: test")
797 807 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
798 808
799 809 # Run the same check twice to verify that the transformer is not
800 810 # disabled after raising.
801 811 with expect_exception_tb, expect_no_cell_output:
802 812 ip.run_cell("'unsafe'")
803 813
804 814 with expect_exception_tb, expect_no_cell_output:
805 815 res = ip.run_cell("'unsafe'")
806 816
807 817 self.assertIsInstance(res.error_before_exec, InputRejected)
808 818
809 819 def test__IPYTHON__():
810 820 # This shouldn't raise a NameError, that's all
811 821 __IPYTHON__
812 822
813 823
814 824 class DummyRepr(object):
815 825 def __repr__(self):
816 826 return "DummyRepr"
817 827
818 828 def _repr_html_(self):
819 829 return "<b>dummy</b>"
820 830
821 831 def _repr_javascript_(self):
822 832 return "console.log('hi');", {'key': 'value'}
823 833
824 834
825 835 def test_user_variables():
826 836 # enable all formatters
827 837 ip.display_formatter.active_types = ip.display_formatter.format_types
828 838
829 839 ip.user_ns['dummy'] = d = DummyRepr()
830 840 keys = {'dummy', 'doesnotexist'}
831 841 r = ip.user_expressions({ key:key for key in keys})
832 842
833 843 nt.assert_equal(keys, set(r.keys()))
834 844 dummy = r['dummy']
835 845 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
836 846 nt.assert_equal(dummy['status'], 'ok')
837 847 data = dummy['data']
838 848 metadata = dummy['metadata']
839 849 nt.assert_equal(data.get('text/html'), d._repr_html_())
840 850 js, jsmd = d._repr_javascript_()
841 851 nt.assert_equal(data.get('application/javascript'), js)
842 852 nt.assert_equal(metadata.get('application/javascript'), jsmd)
843 853
844 854 dne = r['doesnotexist']
845 855 nt.assert_equal(dne['status'], 'error')
846 856 nt.assert_equal(dne['ename'], 'NameError')
847 857
848 858 # back to text only
849 859 ip.display_formatter.active_types = ['text/plain']
850 860
851 861 def test_user_expression():
852 862 # enable all formatters
853 863 ip.display_formatter.active_types = ip.display_formatter.format_types
854 864 query = {
855 865 'a' : '1 + 2',
856 866 'b' : '1/0',
857 867 }
858 868 r = ip.user_expressions(query)
859 869 import pprint
860 870 pprint.pprint(r)
861 871 nt.assert_equal(set(r.keys()), set(query.keys()))
862 872 a = r['a']
863 873 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
864 874 nt.assert_equal(a['status'], 'ok')
865 875 data = a['data']
866 876 metadata = a['metadata']
867 877 nt.assert_equal(data.get('text/plain'), '3')
868 878
869 879 b = r['b']
870 880 nt.assert_equal(b['status'], 'error')
871 881 nt.assert_equal(b['ename'], 'ZeroDivisionError')
872 882
873 883 # back to text only
874 884 ip.display_formatter.active_types = ['text/plain']
875 885
876 886
877 887
878 888
879 889
880 890 class TestSyntaxErrorTransformer(unittest.TestCase):
881 891 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
882 892
883 893 @staticmethod
884 894 def transformer(lines):
885 895 for line in lines:
886 896 pos = line.find('syntaxerror')
887 897 if pos >= 0:
888 898 e = SyntaxError('input contains "syntaxerror"')
889 899 e.text = line
890 900 e.offset = pos + 1
891 901 raise e
892 902 return lines
893 903
894 904 def setUp(self):
895 905 ip.input_transformers_post.append(self.transformer)
896 906
897 907 def tearDown(self):
898 908 ip.input_transformers_post.remove(self.transformer)
899 909
900 910 def test_syntaxerror_input_transformer(self):
901 911 with tt.AssertPrints('1234'):
902 912 ip.run_cell('1234')
903 913 with tt.AssertPrints('SyntaxError: invalid syntax'):
904 914 ip.run_cell('1 2 3') # plain python syntax error
905 915 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
906 916 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
907 917 with tt.AssertPrints('3456'):
908 918 ip.run_cell('3456')
909 919
910 920
911 921
912 922 def test_warning_suppression():
913 923 ip.run_cell("import warnings")
914 924 try:
915 925 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
916 926 ip.run_cell("warnings.warn('asdf')")
917 927 # Here's the real test -- if we run that again, we should get the
918 928 # warning again. Traditionally, each warning was only issued once per
919 929 # IPython session (approximately), even if the user typed in new and
920 930 # different code that should have also triggered the warning, leading
921 931 # to much confusion.
922 932 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
923 933 ip.run_cell("warnings.warn('asdf')")
924 934 finally:
925 935 ip.run_cell("del warnings")
926 936
927 937
928 938 def test_deprecation_warning():
929 939 ip.run_cell("""
930 940 import warnings
931 941 def wrn():
932 942 warnings.warn(
933 943 "I AM A WARNING",
934 944 DeprecationWarning
935 945 )
936 946 """)
937 947 try:
938 948 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
939 949 ip.run_cell("wrn()")
940 950 finally:
941 951 ip.run_cell("del warnings")
942 952 ip.run_cell("del wrn")
943 953
944 954
945 955 class TestImportNoDeprecate(tt.TempFileMixin):
946 956
947 def setup(self):
957 def setUp(self):
948 958 """Make a valid python temp file."""
949 959 self.mktmp("""
950 960 import warnings
951 961 def wrn():
952 962 warnings.warn(
953 963 "I AM A WARNING",
954 964 DeprecationWarning
955 965 )
956 966 """)
967 super().setUp()
957 968
958 969 def test_no_dep(self):
959 970 """
960 971 No deprecation warning should be raised from imported functions
961 972 """
962 973 ip.run_cell("from {} import wrn".format(self.fname))
963 974
964 975 with tt.AssertNotPrints("I AM A WARNING"):
965 976 ip.run_cell("wrn()")
966 977 ip.run_cell("del wrn")
967 978
968 979
969 980 def test_custom_exc_count():
970 981 hook = mock.Mock(return_value=None)
971 982 ip.set_custom_exc((SyntaxError,), hook)
972 983 before = ip.execution_count
973 984 ip.run_cell("def foo()", store_history=True)
974 985 # restore default excepthook
975 986 ip.set_custom_exc((), None)
976 987 nt.assert_equal(hook.call_count, 1)
977 988 nt.assert_equal(ip.execution_count, before + 1)
978 989
979 990
980 991 def test_run_cell_async():
981 992 loop = asyncio.get_event_loop()
982 993 ip.run_cell("import asyncio")
983 994 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
984 995 assert asyncio.iscoroutine(coro)
985 996 result = loop.run_until_complete(coro)
986 997 assert isinstance(result, interactiveshell.ExecutionResult)
987 998 assert result.result == 5
988 999
989 1000
990 1001 def test_should_run_async():
991 1002 assert not ip.should_run_async("a = 5")
992 1003 assert ip.should_run_async("await x")
993 1004 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
@@ -1,559 +1,559 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 from os.path import join as pjoin
22 22 import random
23 23 import string
24 24 import sys
25 25 import textwrap
26 26 import unittest
27 27 from unittest.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.io import capture_output
35 35 from IPython.utils.tempdir import TemporaryDirectory
36 36 from IPython.core import debugger
37 37
38 38 def doctest_refbug():
39 39 """Very nasty problem with references held by multiple runs of a script.
40 40 See: https://github.com/ipython/ipython/issues/141
41 41
42 42 In [1]: _ip.clear_main_mod_cache()
43 43 # random
44 44
45 45 In [2]: %run refbug
46 46
47 47 In [3]: call_f()
48 48 lowercased: hello
49 49
50 50 In [4]: %run refbug
51 51
52 52 In [5]: call_f()
53 53 lowercased: hello
54 54 lowercased: hello
55 55 """
56 56
57 57
58 58 def doctest_run_builtins():
59 59 r"""Check that %run doesn't damage __builtins__.
60 60
61 61 In [1]: import tempfile
62 62
63 63 In [2]: bid1 = id(__builtins__)
64 64
65 65 In [3]: fname = tempfile.mkstemp('.py')[1]
66 66
67 67 In [3]: f = open(fname,'w')
68 68
69 69 In [4]: dummy= f.write('pass\n')
70 70
71 71 In [5]: f.flush()
72 72
73 73 In [6]: t1 = type(__builtins__)
74 74
75 75 In [7]: %run $fname
76 76
77 77 In [7]: f.close()
78 78
79 79 In [8]: bid2 = id(__builtins__)
80 80
81 81 In [9]: t2 = type(__builtins__)
82 82
83 83 In [10]: t1 == t2
84 84 Out[10]: True
85 85
86 86 In [10]: bid1 == bid2
87 87 Out[10]: True
88 88
89 89 In [12]: try:
90 90 ....: os.unlink(fname)
91 91 ....: except:
92 92 ....: pass
93 93 ....:
94 94 """
95 95
96 96
97 97 def doctest_run_option_parser():
98 98 r"""Test option parser in %run.
99 99
100 100 In [1]: %run print_argv.py
101 101 []
102 102
103 103 In [2]: %run print_argv.py print*.py
104 104 ['print_argv.py']
105 105
106 106 In [3]: %run -G print_argv.py print*.py
107 107 ['print*.py']
108 108
109 109 """
110 110
111 111
112 112 @dec.skip_win32
113 113 def doctest_run_option_parser_for_posix():
114 114 r"""Test option parser in %run (Linux/OSX specific).
115 115
116 116 You need double quote to escape glob in POSIX systems:
117 117
118 118 In [1]: %run print_argv.py print\\*.py
119 119 ['print*.py']
120 120
121 121 You can't use quote to escape glob in POSIX systems:
122 122
123 123 In [2]: %run print_argv.py 'print*.py'
124 124 ['print_argv.py']
125 125
126 126 """
127 127
128 128
129 129 @dec.skip_if_not_win32
130 130 def doctest_run_option_parser_for_windows():
131 131 r"""Test option parser in %run (Windows specific).
132 132
133 133 In Windows, you can't escape ``*` `by backslash:
134 134
135 135 In [1]: %run print_argv.py print\\*.py
136 136 ['print\\*.py']
137 137
138 138 You can use quote to escape glob:
139 139
140 140 In [2]: %run print_argv.py 'print*.py'
141 141 ['print*.py']
142 142
143 143 """
144 144
145 145
146 146 def doctest_reset_del():
147 147 """Test that resetting doesn't cause errors in __del__ methods.
148 148
149 149 In [2]: class A(object):
150 150 ...: def __del__(self):
151 151 ...: print(str("Hi"))
152 152 ...:
153 153
154 154 In [3]: a = A()
155 155
156 156 In [4]: get_ipython().reset()
157 157 Hi
158 158
159 159 In [5]: 1+1
160 160 Out[5]: 2
161 161 """
162 162
163 163 # For some tests, it will be handy to organize them in a class with a common
164 164 # setup that makes a temp file
165 165
166 166 class TestMagicRunPass(tt.TempFileMixin):
167 167
168 def setup(self):
168 def setUp(self):
169 169 content = "a = [1,2,3]\nb = 1"
170 170 self.mktmp(content)
171 171
172 172 def run_tmpfile(self):
173 173 _ip = get_ipython()
174 174 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
175 175 # See below and ticket https://bugs.launchpad.net/bugs/366353
176 176 _ip.magic('run %s' % self.fname)
177 177
178 178 def run_tmpfile_p(self):
179 179 _ip = get_ipython()
180 180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 181 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 182 _ip.magic('run -p %s' % self.fname)
183 183
184 184 def test_builtins_id(self):
185 185 """Check that %run doesn't damage __builtins__ """
186 186 _ip = get_ipython()
187 187 # Test that the id of __builtins__ is not modified by %run
188 188 bid1 = id(_ip.user_ns['__builtins__'])
189 189 self.run_tmpfile()
190 190 bid2 = id(_ip.user_ns['__builtins__'])
191 191 nt.assert_equal(bid1, bid2)
192 192
193 193 def test_builtins_type(self):
194 194 """Check that the type of __builtins__ doesn't change with %run.
195 195
196 196 However, the above could pass if __builtins__ was already modified to
197 197 be a dict (it should be a module) by a previous use of %run. So we
198 198 also check explicitly that it really is a module:
199 199 """
200 200 _ip = get_ipython()
201 201 self.run_tmpfile()
202 202 nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys))
203 203
204 204 def test_run_profile( self ):
205 205 """Test that the option -p, which invokes the profiler, do not
206 206 crash by invoking execfile"""
207 207 self.run_tmpfile_p()
208 208
209 209 def test_run_debug_twice(self):
210 210 # https://github.com/ipython/ipython/issues/10028
211 211 _ip = get_ipython()
212 212 with tt.fake_input(['c']):
213 213 _ip.magic('run -d %s' % self.fname)
214 214 with tt.fake_input(['c']):
215 215 _ip.magic('run -d %s' % self.fname)
216 216
217 217 def test_run_debug_twice_with_breakpoint(self):
218 218 """Make a valid python temp file."""
219 219 _ip = get_ipython()
220 220 with tt.fake_input(['b 2', 'c', 'c']):
221 221 _ip.magic('run -d %s' % self.fname)
222 222
223 223 with tt.fake_input(['c']):
224 224 with tt.AssertNotPrints('KeyError'):
225 225 _ip.magic('run -d %s' % self.fname)
226 226
227 227
228 228 class TestMagicRunSimple(tt.TempFileMixin):
229 229
230 230 def test_simpledef(self):
231 231 """Test that simple class definitions work."""
232 232 src = ("class foo: pass\n"
233 233 "def f(): return foo()")
234 234 self.mktmp(src)
235 235 _ip.magic('run %s' % self.fname)
236 236 _ip.run_cell('t = isinstance(f(), foo)')
237 237 nt.assert_true(_ip.user_ns['t'])
238 238
239 239 def test_obj_del(self):
240 240 """Test that object's __del__ methods are called on exit."""
241 241 if sys.platform == 'win32':
242 242 try:
243 243 import win32api
244 244 except ImportError:
245 245 raise SkipTest("Test requires pywin32")
246 246 src = ("class A(object):\n"
247 247 " def __del__(self):\n"
248 248 " print('object A deleted')\n"
249 249 "a = A()\n")
250 250 self.mktmp(src)
251 251 if dec.module_not_available('sqlite3'):
252 252 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
253 253 else:
254 254 err = None
255 255 tt.ipexec_validate(self.fname, 'object A deleted', err)
256 256
257 257 def test_aggressive_namespace_cleanup(self):
258 258 """Test that namespace cleanup is not too aggressive GH-238
259 259
260 260 Returning from another run magic deletes the namespace"""
261 261 # see ticket https://github.com/ipython/ipython/issues/238
262 262
263 263 with tt.TempFileMixin() as empty:
264 264 empty.mktmp('')
265 265 # On Windows, the filename will have \users in it, so we need to use the
266 266 # repr so that the \u becomes \\u.
267 267 src = ("ip = get_ipython()\n"
268 268 "for i in range(5):\n"
269 269 " try:\n"
270 270 " ip.magic(%r)\n"
271 271 " except NameError as e:\n"
272 272 " print(i)\n"
273 273 " break\n" % ('run ' + empty.fname))
274 274 self.mktmp(src)
275 275 _ip.magic('run %s' % self.fname)
276 276 _ip.run_cell('ip == get_ipython()')
277 277 nt.assert_equal(_ip.user_ns['i'], 4)
278 278
279 279 def test_run_second(self):
280 280 """Test that running a second file doesn't clobber the first, gh-3547
281 281 """
282 282 self.mktmp("avar = 1\n"
283 283 "def afunc():\n"
284 284 " return avar\n")
285 285
286 286 with tt.TempFileMixin() as empty:
287 287 empty.mktmp("")
288 288
289 289 _ip.magic('run %s' % self.fname)
290 290 _ip.magic('run %s' % empty.fname)
291 291 nt.assert_equal(_ip.user_ns['afunc'](), 1)
292 292
293 293 @dec.skip_win32
294 294 def test_tclass(self):
295 295 mydir = os.path.dirname(__file__)
296 296 tc = os.path.join(mydir, 'tclass')
297 297 src = ("%%run '%s' C-first\n"
298 298 "%%run '%s' C-second\n"
299 299 "%%run '%s' C-third\n") % (tc, tc, tc)
300 300 self.mktmp(src, '.ipy')
301 301 out = """\
302 302 ARGV 1-: ['C-first']
303 303 ARGV 1-: ['C-second']
304 304 tclass.py: deleting object: C-first
305 305 ARGV 1-: ['C-third']
306 306 tclass.py: deleting object: C-second
307 307 tclass.py: deleting object: C-third
308 308 """
309 309 if dec.module_not_available('sqlite3'):
310 310 err = 'WARNING: IPython History requires SQLite, your history will not be saved\n'
311 311 else:
312 312 err = None
313 313 tt.ipexec_validate(self.fname, out, err)
314 314
315 315 def test_run_i_after_reset(self):
316 316 """Check that %run -i still works after %reset (gh-693)"""
317 317 src = "yy = zz\n"
318 318 self.mktmp(src)
319 319 _ip.run_cell("zz = 23")
320 320 try:
321 321 _ip.magic('run -i %s' % self.fname)
322 322 nt.assert_equal(_ip.user_ns['yy'], 23)
323 323 finally:
324 324 _ip.magic('reset -f')
325 325
326 326 _ip.run_cell("zz = 23")
327 327 try:
328 328 _ip.magic('run -i %s' % self.fname)
329 329 nt.assert_equal(_ip.user_ns['yy'], 23)
330 330 finally:
331 331 _ip.magic('reset -f')
332 332
333 333 def test_unicode(self):
334 334 """Check that files in odd encodings are accepted."""
335 335 mydir = os.path.dirname(__file__)
336 336 na = os.path.join(mydir, 'nonascii.py')
337 337 _ip.magic('run "%s"' % na)
338 338 nt.assert_equal(_ip.user_ns['u'], u'ΠŽΡ‚β„–Π€')
339 339
340 340 def test_run_py_file_attribute(self):
341 341 """Test handling of `__file__` attribute in `%run <file>.py`."""
342 342 src = "t = __file__\n"
343 343 self.mktmp(src)
344 344 _missing = object()
345 345 file1 = _ip.user_ns.get('__file__', _missing)
346 346 _ip.magic('run %s' % self.fname)
347 347 file2 = _ip.user_ns.get('__file__', _missing)
348 348
349 349 # Check that __file__ was equal to the filename in the script's
350 350 # namespace.
351 351 nt.assert_equal(_ip.user_ns['t'], self.fname)
352 352
353 353 # Check that __file__ was not leaked back into user_ns.
354 354 nt.assert_equal(file1, file2)
355 355
356 356 def test_run_ipy_file_attribute(self):
357 357 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
358 358 src = "t = __file__\n"
359 359 self.mktmp(src, ext='.ipy')
360 360 _missing = object()
361 361 file1 = _ip.user_ns.get('__file__', _missing)
362 362 _ip.magic('run %s' % self.fname)
363 363 file2 = _ip.user_ns.get('__file__', _missing)
364 364
365 365 # Check that __file__ was equal to the filename in the script's
366 366 # namespace.
367 367 nt.assert_equal(_ip.user_ns['t'], self.fname)
368 368
369 369 # Check that __file__ was not leaked back into user_ns.
370 370 nt.assert_equal(file1, file2)
371 371
372 372 def test_run_formatting(self):
373 373 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
374 374 src = "pass"
375 375 self.mktmp(src)
376 376 _ip.magic('run -t -N 1 %s' % self.fname)
377 377 _ip.magic('run -t -N 10 %s' % self.fname)
378 378
379 379 def test_ignore_sys_exit(self):
380 380 """Test the -e option to ignore sys.exit()"""
381 381 src = "import sys; sys.exit(1)"
382 382 self.mktmp(src)
383 383 with tt.AssertPrints('SystemExit'):
384 384 _ip.magic('run %s' % self.fname)
385 385
386 386 with tt.AssertNotPrints('SystemExit'):
387 387 _ip.magic('run -e %s' % self.fname)
388 388
389 389 def test_run_nb(self):
390 390 """Test %run notebook.ipynb"""
391 391 from nbformat import v4, writes
392 392 nb = v4.new_notebook(
393 393 cells=[
394 394 v4.new_markdown_cell("The Ultimate Question of Everything"),
395 395 v4.new_code_cell("answer=42")
396 396 ]
397 397 )
398 398 src = writes(nb, version=4)
399 399 self.mktmp(src, ext='.ipynb')
400 400
401 401 _ip.magic("run %s" % self.fname)
402 402
403 403 nt.assert_equal(_ip.user_ns['answer'], 42)
404 404
405 405 def test_file_options(self):
406 406 src = ('import sys\n'
407 407 'a = " ".join(sys.argv[1:])\n')
408 408 self.mktmp(src)
409 409 test_opts = '-x 3 --verbose'
410 410 _ip.run_line_magic("run", '{0} {1}'.format(self.fname, test_opts))
411 411 nt.assert_equal(_ip.user_ns['a'], test_opts)
412 412
413 413
414 414 class TestMagicRunWithPackage(unittest.TestCase):
415 415
416 416 def writefile(self, name, content):
417 417 path = os.path.join(self.tempdir.name, name)
418 418 d = os.path.dirname(path)
419 419 if not os.path.isdir(d):
420 420 os.makedirs(d)
421 421 with open(path, 'w') as f:
422 422 f.write(textwrap.dedent(content))
423 423
424 424 def setUp(self):
425 425 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
426 426 """Temporary (probably) valid python package name."""
427 427
428 428 self.value = int(random.random() * 10000)
429 429
430 430 self.tempdir = TemporaryDirectory()
431 431 self.__orig_cwd = os.getcwd()
432 432 sys.path.insert(0, self.tempdir.name)
433 433
434 434 self.writefile(os.path.join(package, '__init__.py'), '')
435 435 self.writefile(os.path.join(package, 'sub.py'), """
436 436 x = {0!r}
437 437 """.format(self.value))
438 438 self.writefile(os.path.join(package, 'relative.py'), """
439 439 from .sub import x
440 440 """)
441 441 self.writefile(os.path.join(package, 'absolute.py'), """
442 442 from {0}.sub import x
443 443 """.format(package))
444 444 self.writefile(os.path.join(package, 'args.py'), """
445 445 import sys
446 446 a = " ".join(sys.argv[1:])
447 447 """.format(package))
448 448
449 449 def tearDown(self):
450 450 os.chdir(self.__orig_cwd)
451 451 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
452 452 self.tempdir.cleanup()
453 453
454 454 def check_run_submodule(self, submodule, opts=''):
455 455 _ip.user_ns.pop('x', None)
456 456 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
457 457 self.assertEqual(_ip.user_ns['x'], self.value,
458 458 'Variable `x` is not loaded from module `{0}`.'
459 459 .format(submodule))
460 460
461 461 def test_run_submodule_with_absolute_import(self):
462 462 self.check_run_submodule('absolute')
463 463
464 464 def test_run_submodule_with_relative_import(self):
465 465 """Run submodule that has a relative import statement (#2727)."""
466 466 self.check_run_submodule('relative')
467 467
468 468 def test_prun_submodule_with_absolute_import(self):
469 469 self.check_run_submodule('absolute', '-p')
470 470
471 471 def test_prun_submodule_with_relative_import(self):
472 472 self.check_run_submodule('relative', '-p')
473 473
474 474 def with_fake_debugger(func):
475 475 @functools.wraps(func)
476 476 def wrapper(*args, **kwds):
477 477 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
478 478 return func(*args, **kwds)
479 479 return wrapper
480 480
481 481 @with_fake_debugger
482 482 def test_debug_run_submodule_with_absolute_import(self):
483 483 self.check_run_submodule('absolute', '-d')
484 484
485 485 @with_fake_debugger
486 486 def test_debug_run_submodule_with_relative_import(self):
487 487 self.check_run_submodule('relative', '-d')
488 488
489 489 def test_module_options(self):
490 490 _ip.user_ns.pop('a', None)
491 491 test_opts = '-x abc -m test'
492 492 _ip.run_line_magic('run', '-m {0}.args {1}'.format(self.package, test_opts))
493 493 nt.assert_equal(_ip.user_ns['a'], test_opts)
494 494
495 495 def test_module_options_with_separator(self):
496 496 _ip.user_ns.pop('a', None)
497 497 test_opts = '-x abc -m test'
498 498 _ip.run_line_magic('run', '-m {0}.args -- {1}'.format(self.package, test_opts))
499 499 nt.assert_equal(_ip.user_ns['a'], test_opts)
500 500
501 501 def test_run__name__():
502 502 with TemporaryDirectory() as td:
503 503 path = pjoin(td, 'foo.py')
504 504 with open(path, 'w') as f:
505 505 f.write("q = __name__")
506 506
507 507 _ip.user_ns.pop('q', None)
508 508 _ip.magic('run {}'.format(path))
509 509 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
510 510
511 511 _ip.magic('run -n {}'.format(path))
512 512 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
513 513
514 514 try:
515 515 _ip.magic('run -i -n {}'.format(path))
516 516 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
517 517 finally:
518 518 _ip.magic('reset -f')
519 519
520 520
521 521 def test_run_tb():
522 522 """Test traceback offset in %run"""
523 523 with TemporaryDirectory() as td:
524 524 path = pjoin(td, 'foo.py')
525 525 with open(path, 'w') as f:
526 526 f.write('\n'.join([
527 527 "def foo():",
528 528 " return bar()",
529 529 "def bar():",
530 530 " raise RuntimeError('hello!')",
531 531 "foo()",
532 532 ]))
533 533 with capture_output() as io:
534 534 _ip.magic('run {}'.format(path))
535 535 out = io.stdout
536 536 nt.assert_not_in("execfile", out)
537 537 nt.assert_in("RuntimeError", out)
538 538 nt.assert_equal(out.count("---->"), 3)
539 539 del ip.user_ns['bar']
540 540 del ip.user_ns['foo']
541 541
542 542 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
543 543 def test_script_tb():
544 544 """Test traceback offset in `ipython script.py`"""
545 545 with TemporaryDirectory() as td:
546 546 path = pjoin(td, 'foo.py')
547 547 with open(path, 'w') as f:
548 548 f.write('\n'.join([
549 549 "def foo():",
550 550 " return bar()",
551 551 "def bar():",
552 552 " raise RuntimeError('hello!')",
553 553 "foo()",
554 554 ]))
555 555 out, err = tt.ipexec(path)
556 556 nt.assert_not_in("execfile", out)
557 557 nt.assert_in("RuntimeError", out)
558 558 nt.assert_equal(out.count("---->"), 3)
559 559
@@ -1,449 +1,451 b''
1 1 """Tests for autoreload extension.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import os
16 16 import sys
17 17 import tempfile
18 18 import textwrap
19 19 import shutil
20 20 import random
21 21 import time
22 22 from io import StringIO
23 23
24 24 import nose.tools as nt
25 25 import IPython.testing.tools as tt
26 26
27 from unittest import TestCase
28
27 29 from IPython.testing.decorators import skipif
28 30
29 31 from IPython.extensions.autoreload import AutoreloadMagics
30 32 from IPython.core.events import EventManager, pre_run_cell
31 33
32 34 #-----------------------------------------------------------------------------
33 35 # Test fixture
34 36 #-----------------------------------------------------------------------------
35 37
36 38 noop = lambda *a, **kw: None
37 39
38 40 class FakeShell:
39 41
40 42 def __init__(self):
41 43 self.ns = {}
42 44 self.user_ns = self.ns
43 45 self.user_ns_hidden = {}
44 46 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
45 47 self.auto_magics = AutoreloadMagics(shell=self)
46 48 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
47 49
48 50 register_magics = set_hook = noop
49 51
50 52 def run_code(self, code):
51 53 self.events.trigger('pre_run_cell')
52 54 exec(code, self.user_ns)
53 55 self.auto_magics.post_execute_hook()
54 56
55 57 def push(self, items):
56 58 self.ns.update(items)
57 59
58 60 def magic_autoreload(self, parameter):
59 61 self.auto_magics.autoreload(parameter)
60 62
61 63 def magic_aimport(self, parameter, stream=None):
62 64 self.auto_magics.aimport(parameter, stream=stream)
63 65 self.auto_magics.post_execute_hook()
64 66
65 67
66 class Fixture(object):
68 class Fixture(TestCase):
67 69 """Fixture for creating test module files"""
68 70
69 71 test_dir = None
70 72 old_sys_path = None
71 73 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
72 74
73 75 def setUp(self):
74 76 self.test_dir = tempfile.mkdtemp()
75 77 self.old_sys_path = list(sys.path)
76 78 sys.path.insert(0, self.test_dir)
77 79 self.shell = FakeShell()
78 80
79 81 def tearDown(self):
80 82 shutil.rmtree(self.test_dir)
81 83 sys.path = self.old_sys_path
82 84
83 85 self.test_dir = None
84 86 self.old_sys_path = None
85 87 self.shell = None
86 88
87 89 def get_module(self):
88 90 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
89 91 if module_name in sys.modules:
90 92 del sys.modules[module_name]
91 93 file_name = os.path.join(self.test_dir, module_name + ".py")
92 94 return module_name, file_name
93 95
94 96 def write_file(self, filename, content):
95 97 """
96 98 Write a file, and force a timestamp difference of at least one second
97 99
98 100 Notes
99 101 -----
100 102 Python's .pyc files record the timestamp of their compilation
101 103 with a time resolution of one second.
102 104
103 105 Therefore, we need to force a timestamp difference between .py
104 106 and .pyc, without having the .py file be timestamped in the
105 107 future, and without changing the timestamp of the .pyc file
106 108 (because that is stored in the file). The only reliable way
107 109 to achieve this seems to be to sleep.
108 110 """
109 111 content = textwrap.dedent(content)
110 112 # Sleep one second + eps
111 113 time.sleep(1.05)
112 114
113 115 # Write
114 116 with open(filename, 'w') as f:
115 117 f.write(content)
116 118
117 119 def new_module(self, code):
118 120 code = textwrap.dedent(code)
119 121 mod_name, mod_fn = self.get_module()
120 122 with open(mod_fn, 'w') as f:
121 123 f.write(code)
122 124 return mod_name, mod_fn
123 125
124 126 #-----------------------------------------------------------------------------
125 127 # Test automatic reloading
126 128 #-----------------------------------------------------------------------------
127 129
128 130 def pickle_get_current_class(obj):
129 131 """
130 132 Original issue comes from pickle; hence the name.
131 133 """
132 134 name = obj.__class__.__name__
133 135 module_name = getattr(obj, "__module__", None)
134 136 obj2 = sys.modules[module_name]
135 137 for subpath in name.split("."):
136 138 obj2 = getattr(obj2, subpath)
137 139 return obj2
138 140
139 141 class TestAutoreload(Fixture):
140 142
141 143 @skipif(sys.version_info < (3, 6))
142 144 def test_reload_enums(self):
143 145 import enum
144 146 mod_name, mod_fn = self.new_module(textwrap.dedent("""
145 147 from enum import Enum
146 148 class MyEnum(Enum):
147 149 A = 'A'
148 150 B = 'B'
149 151 """))
150 152 self.shell.magic_autoreload("2")
151 153 self.shell.magic_aimport(mod_name)
152 154 self.write_file(mod_fn, textwrap.dedent("""
153 155 from enum import Enum
154 156 class MyEnum(Enum):
155 157 A = 'A'
156 158 B = 'B'
157 159 C = 'C'
158 160 """))
159 161 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
160 162 self.shell.run_code("pass") # trigger another reload
161 163
162 164 def test_reload_class_type(self):
163 165 self.shell.magic_autoreload("2")
164 166 mod_name, mod_fn = self.new_module(
165 167 """
166 168 class Test():
167 169 def meth(self):
168 170 return "old"
169 171 """
170 172 )
171 173 assert "test" not in self.shell.ns
172 174 assert "result" not in self.shell.ns
173 175
174 176 self.shell.run_code("from %s import Test" % mod_name)
175 177 self.shell.run_code("test = Test()")
176 178
177 179 self.write_file(
178 180 mod_fn,
179 181 """
180 182 class Test():
181 183 def meth(self):
182 184 return "new"
183 185 """,
184 186 )
185 187
186 188 test_object = self.shell.ns["test"]
187 189
188 190 # important to trigger autoreload logic !
189 191 self.shell.run_code("pass")
190 192
191 193 test_class = pickle_get_current_class(test_object)
192 194 assert isinstance(test_object, test_class)
193 195
194 196 # extra check.
195 197 self.shell.run_code("import pickle")
196 198 self.shell.run_code("p = pickle.dumps(test)")
197 199
198 200 def test_reload_class_attributes(self):
199 201 self.shell.magic_autoreload("2")
200 202 mod_name, mod_fn = self.new_module(textwrap.dedent("""
201 203 class MyClass:
202 204
203 205 def __init__(self, a=10):
204 206 self.a = a
205 207 self.b = 22
206 208 # self.toto = 33
207 209
208 210 def square(self):
209 211 print('compute square')
210 212 return self.a*self.a
211 213 """
212 214 )
213 215 )
214 216 self.shell.run_code("from %s import MyClass" % mod_name)
215 217 self.shell.run_code("first = MyClass(5)")
216 218 self.shell.run_code("first.square()")
217 219 with nt.assert_raises(AttributeError):
218 220 self.shell.run_code("first.cube()")
219 221 with nt.assert_raises(AttributeError):
220 222 self.shell.run_code("first.power(5)")
221 223 self.shell.run_code("first.b")
222 224 with nt.assert_raises(AttributeError):
223 225 self.shell.run_code("first.toto")
224 226
225 227 # remove square, add power
226 228
227 229 self.write_file(
228 230 mod_fn,
229 231 textwrap.dedent(
230 232 """
231 233 class MyClass:
232 234
233 235 def __init__(self, a=10):
234 236 self.a = a
235 237 self.b = 11
236 238
237 239 def power(self, p):
238 240 print('compute power '+str(p))
239 241 return self.a**p
240 242 """
241 243 ),
242 244 )
243 245
244 246 self.shell.run_code("second = MyClass(5)")
245 247
246 248 for object_name in {'first', 'second'}:
247 249 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
248 250 with nt.assert_raises(AttributeError):
249 251 self.shell.run_code("{object_name}.cube()".format(object_name=object_name))
250 252 with nt.assert_raises(AttributeError):
251 253 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
252 254 self.shell.run_code("{object_name}.b".format(object_name=object_name))
253 255 self.shell.run_code("{object_name}.a".format(object_name=object_name))
254 256 with nt.assert_raises(AttributeError):
255 257 self.shell.run_code("{object_name}.toto".format(object_name=object_name))
256 258
257 259 def _check_smoketest(self, use_aimport=True):
258 260 """
259 261 Functional test for the automatic reloader using either
260 262 '%autoreload 1' or '%autoreload 2'
261 263 """
262 264
263 265 mod_name, mod_fn = self.new_module("""
264 266 x = 9
265 267
266 268 z = 123 # this item will be deleted
267 269
268 270 def foo(y):
269 271 return y + 3
270 272
271 273 class Baz(object):
272 274 def __init__(self, x):
273 275 self.x = x
274 276 def bar(self, y):
275 277 return self.x + y
276 278 @property
277 279 def quux(self):
278 280 return 42
279 281 def zzz(self):
280 282 '''This method will be deleted below'''
281 283 return 99
282 284
283 285 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
284 286 def foo(self):
285 287 return 1
286 288 """)
287 289
288 290 #
289 291 # Import module, and mark for reloading
290 292 #
291 293 if use_aimport:
292 294 self.shell.magic_autoreload("1")
293 295 self.shell.magic_aimport(mod_name)
294 296 stream = StringIO()
295 297 self.shell.magic_aimport("", stream=stream)
296 298 nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
297 299
298 300 with nt.assert_raises(ImportError):
299 301 self.shell.magic_aimport("tmpmod_as318989e89ds")
300 302 else:
301 303 self.shell.magic_autoreload("2")
302 304 self.shell.run_code("import %s" % mod_name)
303 305 stream = StringIO()
304 306 self.shell.magic_aimport("", stream=stream)
305 307 nt.assert_true("Modules to reload:\nall-except-skipped" in
306 308 stream.getvalue())
307 309 nt.assert_in(mod_name, self.shell.ns)
308 310
309 311 mod = sys.modules[mod_name]
310 312
311 313 #
312 314 # Test module contents
313 315 #
314 316 old_foo = mod.foo
315 317 old_obj = mod.Baz(9)
316 318 old_obj2 = mod.Bar()
317 319
318 320 def check_module_contents():
319 321 nt.assert_equal(mod.x, 9)
320 322 nt.assert_equal(mod.z, 123)
321 323
322 324 nt.assert_equal(old_foo(0), 3)
323 325 nt.assert_equal(mod.foo(0), 3)
324 326
325 327 obj = mod.Baz(9)
326 328 nt.assert_equal(old_obj.bar(1), 10)
327 329 nt.assert_equal(obj.bar(1), 10)
328 330 nt.assert_equal(obj.quux, 42)
329 331 nt.assert_equal(obj.zzz(), 99)
330 332
331 333 obj2 = mod.Bar()
332 334 nt.assert_equal(old_obj2.foo(), 1)
333 335 nt.assert_equal(obj2.foo(), 1)
334 336
335 337 check_module_contents()
336 338
337 339 #
338 340 # Simulate a failed reload: no reload should occur and exactly
339 341 # one error message should be printed
340 342 #
341 343 self.write_file(mod_fn, """
342 344 a syntax error
343 345 """)
344 346
345 347 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
346 348 self.shell.run_code("pass") # trigger reload
347 349 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
348 350 self.shell.run_code("pass") # trigger another reload
349 351 check_module_contents()
350 352
351 353 #
352 354 # Rewrite module (this time reload should succeed)
353 355 #
354 356 self.write_file(mod_fn, """
355 357 x = 10
356 358
357 359 def foo(y):
358 360 return y + 4
359 361
360 362 class Baz(object):
361 363 def __init__(self, x):
362 364 self.x = x
363 365 def bar(self, y):
364 366 return self.x + y + 1
365 367 @property
366 368 def quux(self):
367 369 return 43
368 370
369 371 class Bar: # old-style class
370 372 def foo(self):
371 373 return 2
372 374 """)
373 375
374 376 def check_module_contents():
375 377 nt.assert_equal(mod.x, 10)
376 378 nt.assert_false(hasattr(mod, 'z'))
377 379
378 380 nt.assert_equal(old_foo(0), 4) # superreload magic!
379 381 nt.assert_equal(mod.foo(0), 4)
380 382
381 383 obj = mod.Baz(9)
382 384 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
383 385 nt.assert_equal(obj.bar(1), 11)
384 386
385 387 nt.assert_equal(old_obj.quux, 43)
386 388 nt.assert_equal(obj.quux, 43)
387 389
388 390 nt.assert_false(hasattr(old_obj, 'zzz'))
389 391 nt.assert_false(hasattr(obj, 'zzz'))
390 392
391 393 obj2 = mod.Bar()
392 394 nt.assert_equal(old_obj2.foo(), 2)
393 395 nt.assert_equal(obj2.foo(), 2)
394 396
395 397 self.shell.run_code("pass") # trigger reload
396 398 check_module_contents()
397 399
398 400 #
399 401 # Another failure case: deleted file (shouldn't reload)
400 402 #
401 403 os.unlink(mod_fn)
402 404
403 405 self.shell.run_code("pass") # trigger reload
404 406 check_module_contents()
405 407
406 408 #
407 409 # Disable autoreload and rewrite module: no reload should occur
408 410 #
409 411 if use_aimport:
410 412 self.shell.magic_aimport("-" + mod_name)
411 413 stream = StringIO()
412 414 self.shell.magic_aimport("", stream=stream)
413 415 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
414 416 stream.getvalue())
415 417
416 418 # This should succeed, although no such module exists
417 419 self.shell.magic_aimport("-tmpmod_as318989e89ds")
418 420 else:
419 421 self.shell.magic_autoreload("0")
420 422
421 423 self.write_file(mod_fn, """
422 424 x = -99
423 425 """)
424 426
425 427 self.shell.run_code("pass") # trigger reload
426 428 self.shell.run_code("pass")
427 429 check_module_contents()
428 430
429 431 #
430 432 # Re-enable autoreload: reload should now occur
431 433 #
432 434 if use_aimport:
433 435 self.shell.magic_aimport(mod_name)
434 436 else:
435 437 self.shell.magic_autoreload("")
436 438
437 439 self.shell.run_code("pass") # trigger reload
438 440 nt.assert_equal(mod.x, -99)
439 441
440 442 def test_smoketest_aimport(self):
441 443 self._check_smoketest(use_aimport=True)
442 444
443 445 def test_smoketest_autoreload(self):
444 446 self._check_smoketest(use_aimport=False)
445 447
446 448
447 449
448 450
449 451
@@ -1,135 +1,135 b''
1 1 # encoding: utf-8
2 2 """
3 3 Tests for testing.tools
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import unittest
19 19
20 20 import nose.tools as nt
21 21
22 22 from IPython.testing import decorators as dec
23 23 from IPython.testing import tools as tt
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Tests
27 27 #-----------------------------------------------------------------------------
28 28
29 29 @dec.skip_win32
30 30 def test_full_path_posix():
31 31 spath = '/foo/bar.py'
32 32 result = tt.full_path(spath,['a.txt','b.txt'])
33 33 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
34 34 spath = '/foo'
35 35 result = tt.full_path(spath,['a.txt','b.txt'])
36 36 nt.assert_equal(result, ['/a.txt', '/b.txt'])
37 37 result = tt.full_path(spath,'a.txt')
38 38 nt.assert_equal(result, ['/a.txt'])
39 39
40 40
41 41 @dec.skip_if_not_win32
42 42 def test_full_path_win32():
43 43 spath = 'c:\\foo\\bar.py'
44 44 result = tt.full_path(spath,['a.txt','b.txt'])
45 45 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
46 46 spath = 'c:\\foo'
47 47 result = tt.full_path(spath,['a.txt','b.txt'])
48 48 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
49 49 result = tt.full_path(spath,'a.txt')
50 50 nt.assert_equal(result, ['c:\\a.txt'])
51 51
52 52
53 53 def test_parser():
54 54 err = ("FAILED (errors=1)", 1, 0)
55 55 fail = ("FAILED (failures=1)", 0, 1)
56 56 both = ("FAILED (errors=1, failures=1)", 1, 1)
57 57 for txt, nerr, nfail in [err, fail, both]:
58 58 nerr1, nfail1 = tt.parse_test_output(txt)
59 59 nt.assert_equal(nerr, nerr1)
60 60 nt.assert_equal(nfail, nfail1)
61 61
62 62
63 63 def test_temp_pyfile():
64 64 src = 'pass\n'
65 65 fname = tt.temp_pyfile(src)
66 66 assert os.path.isfile(fname)
67 67 with open(fname) as fh2:
68 68 src2 = fh2.read()
69 69 nt.assert_equal(src2, src)
70 70
71 71 class TestAssertPrints(unittest.TestCase):
72 72 def test_passing(self):
73 73 with tt.AssertPrints("abc"):
74 74 print("abcd")
75 75 print("def")
76 76 print(b"ghi")
77 77
78 78 def test_failing(self):
79 79 def func():
80 80 with tt.AssertPrints("abc"):
81 81 print("acd")
82 82 print("def")
83 83 print(b"ghi")
84 84
85 85 self.assertRaises(AssertionError, func)
86 86
87 87
88 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
88 class Test_ipexec_validate(tt.TempFileMixin):
89 89 def test_main_path(self):
90 90 """Test with only stdout results.
91 91 """
92 92 self.mktmp("print('A')\n"
93 93 "print('B')\n"
94 94 )
95 95 out = "A\nB"
96 96 tt.ipexec_validate(self.fname, out)
97 97
98 98 def test_main_path2(self):
99 99 """Test with only stdout results, expecting windows line endings.
100 100 """
101 101 self.mktmp("print('A')\n"
102 102 "print('B')\n"
103 103 )
104 104 out = "A\r\nB"
105 105 tt.ipexec_validate(self.fname, out)
106 106
107 107 def test_exception_path(self):
108 108 """Test exception path in exception_validate.
109 109 """
110 110 self.mktmp("import sys\n"
111 111 "print('A')\n"
112 112 "print('B')\n"
113 113 "print('C', file=sys.stderr)\n"
114 114 "print('D', file=sys.stderr)\n"
115 115 )
116 116 out = "A\nB"
117 117 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
118 118
119 119 def test_exception_path2(self):
120 120 """Test exception path in exception_validate, expecting windows line endings.
121 121 """
122 122 self.mktmp("import sys\n"
123 123 "print('A')\n"
124 124 "print('B')\n"
125 125 "print('C', file=sys.stderr)\n"
126 126 "print('D', file=sys.stderr)\n"
127 127 )
128 128 out = "A\r\nB"
129 129 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
130 130
131 131
132 132 def tearDown(self):
133 133 # tear down correctly the mixin,
134 134 # unittest.TestCase.tearDown does nothing
135 135 tt.TempFileMixin.tearDown(self)
@@ -1,470 +1,471 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 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import os
13 13 import re
14 14 import sys
15 15 import tempfile
16 import unittest
16 17
17 18 from contextlib import contextmanager
18 19 from io import StringIO
19 20 from subprocess import Popen, PIPE
20 21 from unittest.mock import patch
21 22
22 23 try:
23 24 # These tools are used by parts of the runtime, so we make the nose
24 25 # dependency optional at this point. Nose is a hard dependency to run the
25 26 # test suite, but NOT to use ipython itself.
26 27 import nose.tools as nt
27 28 has_nose = True
28 29 except ImportError:
29 30 has_nose = False
30 31
31 32 from traitlets.config.loader import Config
32 33 from IPython.utils.process import get_output_error_code
33 34 from IPython.utils.text import list_strings
34 35 from IPython.utils.io import temp_pyfile, Tee
35 36 from IPython.utils import py3compat
36 37
37 38 from . import decorators as dec
38 39 from . import skipdoctest
39 40
40 41
41 42 # The docstring for full_path doctests differently on win32 (different path
42 43 # separator) so just skip the doctest there. The example remains informative.
43 44 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
44 45
45 46 @doctest_deco
46 47 def full_path(startPath,files):
47 48 """Make full paths for all the listed files, based on startPath.
48 49
49 50 Only the base part of startPath is kept, since this routine is typically
50 51 used with a script's ``__file__`` variable as startPath. The base of startPath
51 52 is then prepended to all the listed files, forming the output list.
52 53
53 54 Parameters
54 55 ----------
55 56 startPath : string
56 57 Initial path to use as the base for the results. This path is split
57 58 using os.path.split() and only its first component is kept.
58 59
59 60 files : string or list
60 61 One or more files.
61 62
62 63 Examples
63 64 --------
64 65
65 66 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
66 67 ['/foo/a.txt', '/foo/b.txt']
67 68
68 69 >>> full_path('/foo',['a.txt','b.txt'])
69 70 ['/a.txt', '/b.txt']
70 71
71 72 If a single file is given, the output is still a list::
72 73
73 74 >>> full_path('/foo','a.txt')
74 75 ['/a.txt']
75 76 """
76 77
77 78 files = list_strings(files)
78 79 base = os.path.split(startPath)[0]
79 80 return [ os.path.join(base,f) for f in files ]
80 81
81 82
82 83 def parse_test_output(txt):
83 84 """Parse the output of a test run and return errors, failures.
84 85
85 86 Parameters
86 87 ----------
87 88 txt : str
88 89 Text output of a test run, assumed to contain a line of one of the
89 90 following forms::
90 91
91 92 'FAILED (errors=1)'
92 93 'FAILED (failures=1)'
93 94 'FAILED (errors=1, failures=1)'
94 95
95 96 Returns
96 97 -------
97 98 nerr, nfail
98 99 number of errors and failures.
99 100 """
100 101
101 102 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
102 103 if err_m:
103 104 nerr = int(err_m.group(1))
104 105 nfail = 0
105 106 return nerr, nfail
106 107
107 108 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
108 109 if fail_m:
109 110 nerr = 0
110 111 nfail = int(fail_m.group(1))
111 112 return nerr, nfail
112 113
113 114 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
114 115 re.MULTILINE)
115 116 if both_m:
116 117 nerr = int(both_m.group(1))
117 118 nfail = int(both_m.group(2))
118 119 return nerr, nfail
119 120
120 121 # If the input didn't match any of these forms, assume no error/failures
121 122 return 0, 0
122 123
123 124
124 125 # So nose doesn't think this is a test
125 126 parse_test_output.__test__ = False
126 127
127 128
128 129 def default_argv():
129 130 """Return a valid default argv for creating testing instances of ipython"""
130 131
131 132 return ['--quick', # so no config file is loaded
132 133 # Other defaults to minimize side effects on stdout
133 134 '--colors=NoColor', '--no-term-title','--no-banner',
134 135 '--autocall=0']
135 136
136 137
137 138 def default_config():
138 139 """Return a config object with good defaults for testing."""
139 140 config = Config()
140 141 config.TerminalInteractiveShell.colors = 'NoColor'
141 142 config.TerminalTerminalInteractiveShell.term_title = False,
142 143 config.TerminalInteractiveShell.autocall = 0
143 144 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
144 145 config.HistoryManager.hist_file = f.name
145 146 f.close()
146 147 config.HistoryManager.db_cache_size = 10000
147 148 return config
148 149
149 150
150 151 def get_ipython_cmd(as_string=False):
151 152 """
152 153 Return appropriate IPython command line name. By default, this will return
153 154 a list that can be used with subprocess.Popen, for example, but passing
154 155 `as_string=True` allows for returning the IPython command as a string.
155 156
156 157 Parameters
157 158 ----------
158 159 as_string: bool
159 160 Flag to allow to return the command as a string.
160 161 """
161 162 ipython_cmd = [sys.executable, "-m", "IPython"]
162 163
163 164 if as_string:
164 165 ipython_cmd = " ".join(ipython_cmd)
165 166
166 167 return ipython_cmd
167 168
168 169 def ipexec(fname, options=None, commands=()):
169 170 """Utility to call 'ipython filename'.
170 171
171 172 Starts IPython with a minimal and safe configuration to make startup as fast
172 173 as possible.
173 174
174 175 Note that this starts IPython in a subprocess!
175 176
176 177 Parameters
177 178 ----------
178 179 fname : str
179 180 Name of file to be executed (should have .py or .ipy extension).
180 181
181 182 options : optional, list
182 183 Extra command-line flags to be passed to IPython.
183 184
184 185 commands : optional, list
185 186 Commands to send in on stdin
186 187
187 188 Returns
188 189 -------
189 190 ``(stdout, stderr)`` of ipython subprocess.
190 191 """
191 192 if options is None: options = []
192 193
193 194 cmdargs = default_argv() + options
194 195
195 196 test_dir = os.path.dirname(__file__)
196 197
197 198 ipython_cmd = get_ipython_cmd()
198 199 # Absolute path for filename
199 200 full_fname = os.path.join(test_dir, fname)
200 201 full_cmd = ipython_cmd + cmdargs + [full_fname]
201 202 env = os.environ.copy()
202 203 # FIXME: ignore all warnings in ipexec while we have shims
203 204 # should we keep suppressing warnings here, even after removing shims?
204 205 env['PYTHONWARNINGS'] = 'ignore'
205 206 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
206 207 for k, v in env.items():
207 208 # Debug a bizarre failure we've seen on Windows:
208 209 # TypeError: environment can only contain strings
209 210 if not isinstance(v, str):
210 211 print(k, v)
211 212 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
212 213 out, err = p.communicate(input=py3compat.encode('\n'.join(commands)) or None)
213 214 out, err = py3compat.decode(out), py3compat.decode(err)
214 215 # `import readline` causes 'ESC[?1034h' to be output sometimes,
215 216 # so strip that out before doing comparisons
216 217 if out:
217 218 out = re.sub(r'\x1b\[[^h]+h', '', out)
218 219 return out, err
219 220
220 221
221 222 def ipexec_validate(fname, expected_out, expected_err='',
222 223 options=None, commands=()):
223 224 """Utility to call 'ipython filename' and validate output/error.
224 225
225 226 This function raises an AssertionError if the validation fails.
226 227
227 228 Note that this starts IPython in a subprocess!
228 229
229 230 Parameters
230 231 ----------
231 232 fname : str
232 233 Name of the file to be executed (should have .py or .ipy extension).
233 234
234 235 expected_out : str
235 236 Expected stdout of the process.
236 237
237 238 expected_err : optional, str
238 239 Expected stderr of the process.
239 240
240 241 options : optional, list
241 242 Extra command-line flags to be passed to IPython.
242 243
243 244 Returns
244 245 -------
245 246 None
246 247 """
247 248
248 249 import nose.tools as nt
249 250
250 251 out, err = ipexec(fname, options, commands)
251 252 #print 'OUT', out # dbg
252 253 #print 'ERR', err # dbg
253 254 # If there are any errors, we must check those before stdout, as they may be
254 255 # more informative than simply having an empty stdout.
255 256 if err:
256 257 if expected_err:
257 258 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
258 259 else:
259 260 raise ValueError('Running file %r produced error: %r' %
260 261 (fname, err))
261 262 # If no errors or output on stderr was expected, match stdout
262 263 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
263 264
264 265
265 class TempFileMixin(object):
266 class TempFileMixin(unittest.TestCase):
266 267 """Utility class to create temporary Python/IPython files.
267 268
268 269 Meant as a mixin class for test cases."""
269 270
270 271 def mktmp(self, src, ext='.py'):
271 272 """Make a valid python temp file."""
272 273 fname = temp_pyfile(src, ext)
273 274 if not hasattr(self, 'tmps'):
274 275 self.tmps=[]
275 276 self.tmps.append(fname)
276 277 self.fname = fname
277 278
278 279 def tearDown(self):
279 280 # If the tmpfile wasn't made because of skipped tests, like in
280 281 # win32, there's nothing to cleanup.
281 282 if hasattr(self, 'tmps'):
282 283 for fname in self.tmps:
283 284 # If the tmpfile wasn't made because of skipped tests, like in
284 285 # win32, there's nothing to cleanup.
285 286 try:
286 287 os.unlink(fname)
287 288 except:
288 289 # On Windows, even though we close the file, we still can't
289 290 # delete it. I have no clue why
290 291 pass
291 292
292 293 def __enter__(self):
293 294 return self
294 295
295 296 def __exit__(self, exc_type, exc_value, traceback):
296 297 self.tearDown()
297 298
298 299
299 300 pair_fail_msg = ("Testing {0}\n\n"
300 301 "In:\n"
301 302 " {1!r}\n"
302 303 "Expected:\n"
303 304 " {2!r}\n"
304 305 "Got:\n"
305 306 " {3!r}\n")
306 307 def check_pairs(func, pairs):
307 308 """Utility function for the common case of checking a function with a
308 309 sequence of input/output pairs.
309 310
310 311 Parameters
311 312 ----------
312 313 func : callable
313 314 The function to be tested. Should accept a single argument.
314 315 pairs : iterable
315 316 A list of (input, expected_output) tuples.
316 317
317 318 Returns
318 319 -------
319 320 None. Raises an AssertionError if any output does not match the expected
320 321 value.
321 322 """
322 323 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
323 324 for inp, expected in pairs:
324 325 out = func(inp)
325 326 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
326 327
327 328
328 329 MyStringIO = StringIO
329 330
330 331 _re_type = type(re.compile(r''))
331 332
332 333 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
333 334 -------
334 335 {2!s}
335 336 -------
336 337 """
337 338
338 339 class AssertPrints(object):
339 340 """Context manager for testing that code prints certain text.
340 341
341 342 Examples
342 343 --------
343 344 >>> with AssertPrints("abc", suppress=False):
344 345 ... print("abcd")
345 346 ... print("def")
346 347 ...
347 348 abcd
348 349 def
349 350 """
350 351 def __init__(self, s, channel='stdout', suppress=True):
351 352 self.s = s
352 353 if isinstance(self.s, (str, _re_type)):
353 354 self.s = [self.s]
354 355 self.channel = channel
355 356 self.suppress = suppress
356 357
357 358 def __enter__(self):
358 359 self.orig_stream = getattr(sys, self.channel)
359 360 self.buffer = MyStringIO()
360 361 self.tee = Tee(self.buffer, channel=self.channel)
361 362 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
362 363
363 364 def __exit__(self, etype, value, traceback):
364 365 try:
365 366 if value is not None:
366 367 # If an error was raised, don't check anything else
367 368 return False
368 369 self.tee.flush()
369 370 setattr(sys, self.channel, self.orig_stream)
370 371 printed = self.buffer.getvalue()
371 372 for s in self.s:
372 373 if isinstance(s, _re_type):
373 374 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
374 375 else:
375 376 assert s in printed, notprinted_msg.format(s, self.channel, printed)
376 377 return False
377 378 finally:
378 379 self.tee.close()
379 380
380 381 printed_msg = """Found {0!r} in printed output (on {1}):
381 382 -------
382 383 {2!s}
383 384 -------
384 385 """
385 386
386 387 class AssertNotPrints(AssertPrints):
387 388 """Context manager for checking that certain output *isn't* produced.
388 389
389 390 Counterpart of AssertPrints"""
390 391 def __exit__(self, etype, value, traceback):
391 392 try:
392 393 if value is not None:
393 394 # If an error was raised, don't check anything else
394 395 self.tee.close()
395 396 return False
396 397 self.tee.flush()
397 398 setattr(sys, self.channel, self.orig_stream)
398 399 printed = self.buffer.getvalue()
399 400 for s in self.s:
400 401 if isinstance(s, _re_type):
401 402 assert not s.search(printed),printed_msg.format(
402 403 s.pattern, self.channel, printed)
403 404 else:
404 405 assert s not in printed, printed_msg.format(
405 406 s, self.channel, printed)
406 407 return False
407 408 finally:
408 409 self.tee.close()
409 410
410 411 @contextmanager
411 412 def mute_warn():
412 413 from IPython.utils import warn
413 414 save_warn = warn.warn
414 415 warn.warn = lambda *a, **kw: None
415 416 try:
416 417 yield
417 418 finally:
418 419 warn.warn = save_warn
419 420
420 421 @contextmanager
421 422 def make_tempfile(name):
422 423 """ Create an empty, named, temporary file for the duration of the context.
423 424 """
424 425 open(name, 'w').close()
425 426 try:
426 427 yield
427 428 finally:
428 429 os.unlink(name)
429 430
430 431 def fake_input(inputs):
431 432 """Temporarily replace the input() function to return the given values
432 433
433 434 Use as a context manager:
434 435
435 436 with fake_input(['result1', 'result2']):
436 437 ...
437 438
438 439 Values are returned in order. If input() is called again after the last value
439 440 was used, EOFError is raised.
440 441 """
441 442 it = iter(inputs)
442 443 def mock_input(prompt=''):
443 444 try:
444 445 return next(it)
445 446 except StopIteration:
446 447 raise EOFError('No more inputs given')
447 448
448 449 return patch('builtins.input', mock_input)
449 450
450 451 def help_output_test(subcommand=''):
451 452 """test that `ipython [subcommand] -h` works"""
452 453 cmd = get_ipython_cmd() + [subcommand, '-h']
453 454 out, err, rc = get_output_error_code(cmd)
454 455 nt.assert_equal(rc, 0, err)
455 456 nt.assert_not_in("Traceback", err)
456 457 nt.assert_in("Options", out)
457 458 nt.assert_in("--help-all", out)
458 459 return out, err
459 460
460 461
461 462 def help_all_output_test(subcommand=''):
462 463 """test that `ipython [subcommand] --help-all` works"""
463 464 cmd = get_ipython_cmd() + [subcommand, '--help-all']
464 465 out, err, rc = get_output_error_code(cmd)
465 466 nt.assert_equal(rc, 0, err)
466 467 nt.assert_not_in("Traceback", err)
467 468 nt.assert_in("Options", out)
468 469 nt.assert_in("Class", out)
469 470 return out, err
470 471
@@ -1,145 +1,145 b''
1 1 # encoding: utf-8
2 2 """
3 3 Tests for platutils.py
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import sys
18 18 import os
19 19 from unittest import TestCase
20 20
21 21 import nose.tools as nt
22 22
23 23 from IPython.utils.process import (find_cmd, FindCmdError, arg_split,
24 24 system, getoutput, getoutputerror,
25 25 get_output_error_code)
26 26 from IPython.testing import decorators as dec
27 27 from IPython.testing import tools as tt
28 28
29 29 python = os.path.basename(sys.executable)
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Tests
33 33 #-----------------------------------------------------------------------------
34 34
35 35
36 36 @dec.skip_win32
37 37 def test_find_cmd_ls():
38 38 """Make sure we can find the full path to ls."""
39 39 path = find_cmd('ls')
40 40 nt.assert_true(path.endswith('ls'))
41 41
42 42
43 43 def has_pywin32():
44 44 try:
45 45 import win32api
46 46 except ImportError:
47 47 return False
48 48 return True
49 49
50 50
51 51 @dec.onlyif(has_pywin32, "This test requires win32api to run")
52 52 def test_find_cmd_pythonw():
53 53 """Try to find pythonw on Windows."""
54 54 path = find_cmd('pythonw')
55 55 assert path.lower().endswith('pythonw.exe'), path
56 56
57 57
58 58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
59 59 "This test runs on posix or in win32 with win32api installed")
60 60 def test_find_cmd_fail():
61 61 """Make sure that FindCmdError is raised if we can't find the cmd."""
62 62 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
63 63
64 64
65 65 @dec.skip_win32
66 66 def test_arg_split():
67 67 """Ensure that argument lines are correctly split like in a shell."""
68 68 tests = [['hi', ['hi']],
69 69 [u'hi', [u'hi']],
70 70 ['hello there', ['hello', 'there']],
71 71 # \u01ce == \N{LATIN SMALL LETTER A WITH CARON}
72 72 # Do not use \N because the tests crash with syntax error in
73 73 # some cases, for example windows python2.6.
74 74 [u'h\u01cello', [u'h\u01cello']],
75 75 ['something "with quotes"', ['something', '"with quotes"']],
76 76 ]
77 77 for argstr, argv in tests:
78 78 nt.assert_equal(arg_split(argstr), argv)
79 79
80 80 @dec.skip_if_not_win32
81 81 def test_arg_split_win32():
82 82 """Ensure that argument lines are correctly split like in a shell."""
83 83 tests = [['hi', ['hi']],
84 84 [u'hi', [u'hi']],
85 85 ['hello there', ['hello', 'there']],
86 86 [u'h\u01cello', [u'h\u01cello']],
87 87 ['something "with quotes"', ['something', 'with quotes']],
88 88 ]
89 89 for argstr, argv in tests:
90 90 nt.assert_equal(arg_split(argstr), argv)
91 91
92 92
93 class SubProcessTestCase(TestCase, tt.TempFileMixin):
93 class SubProcessTestCase(tt.TempFileMixin):
94 94 def setUp(self):
95 95 """Make a valid python temp file."""
96 96 lines = [ "import sys",
97 97 "print('on stdout', end='', file=sys.stdout)",
98 98 "print('on stderr', end='', file=sys.stderr)",
99 99 "sys.stdout.flush()",
100 100 "sys.stderr.flush()"]
101 101 self.mktmp('\n'.join(lines))
102 102
103 103 def test_system(self):
104 104 status = system('%s "%s"' % (python, self.fname))
105 105 self.assertEqual(status, 0)
106 106
107 107 def test_system_quotes(self):
108 108 status = system('%s -c "import sys"' % python)
109 109 self.assertEqual(status, 0)
110 110
111 111 def test_getoutput(self):
112 112 out = getoutput('%s "%s"' % (python, self.fname))
113 113 # we can't rely on the order the line buffered streams are flushed
114 114 try:
115 115 self.assertEqual(out, 'on stderron stdout')
116 116 except AssertionError:
117 117 self.assertEqual(out, 'on stdouton stderr')
118 118
119 119 def test_getoutput_quoted(self):
120 120 out = getoutput('%s -c "print (1)"' % python)
121 121 self.assertEqual(out.strip(), '1')
122 122
123 123 #Invalid quoting on windows
124 124 @dec.skip_win32
125 125 def test_getoutput_quoted2(self):
126 126 out = getoutput("%s -c 'print (1)'" % python)
127 127 self.assertEqual(out.strip(), '1')
128 128 out = getoutput("%s -c 'print (\"1\")'" % python)
129 129 self.assertEqual(out.strip(), '1')
130 130
131 131 def test_getoutput_error(self):
132 132 out, err = getoutputerror('%s "%s"' % (python, self.fname))
133 133 self.assertEqual(out, 'on stdout')
134 134 self.assertEqual(err, 'on stderr')
135 135
136 136 def test_get_output_error_code(self):
137 137 quiet_exit = '%s -c "import sys; sys.exit(1)"' % python
138 138 out, err, code = get_output_error_code(quiet_exit)
139 139 self.assertEqual(out, '')
140 140 self.assertEqual(err, '')
141 141 self.assertEqual(code, 1)
142 142 out, err, code = get_output_error_code('%s "%s"' % (python, self.fname))
143 143 self.assertEqual(out, 'on stdout')
144 144 self.assertEqual(err, 'on stderr')
145 145 self.assertEqual(code, 0)
General Comments 0
You need to be logged in to leave comments. Login now