##// END OF EJS Templates
move custom exception at module....
Matthias Bussonnier -
Show More
@@ -1,902 +1,903 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 ast
13 13 import os
14 14 import signal
15 15 import shutil
16 16 import sys
17 17 import tempfile
18 18 import unittest
19 19 try:
20 20 from unittest import mock
21 21 except ImportError:
22 22 import mock
23 23 from os.path import join
24 24
25 25 import nose.tools as nt
26 26
27 27 from IPython.core.error import InputRejected
28 28 from IPython.core.inputtransformer import InputTransformer
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 import io
34 34 from IPython.utils.process import find_cmd
35 35 from IPython.utils import py3compat
36 36 from IPython.utils.py3compat import unicode_type, PY3
37 37
38 38 if PY3:
39 39 from io import StringIO
40 40 else:
41 41 from StringIO import StringIO
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Globals
45 45 #-----------------------------------------------------------------------------
46 46 # This is used by every single test, no point repeating it ad nauseam
47 47 ip = get_ipython()
48 48
49 49 #-----------------------------------------------------------------------------
50 50 # Tests
51 51 #-----------------------------------------------------------------------------
52 52
53 class DerivedInterrupt(KeyboardInterrupt):
54 pass
55
53 56 class InteractiveShellTestCase(unittest.TestCase):
54 57 def test_naked_string_cells(self):
55 58 """Test that cells with only naked strings are fully executed"""
56 59 # First, single-line inputs
57 60 ip.run_cell('"a"\n')
58 61 self.assertEqual(ip.user_ns['_'], 'a')
59 62 # And also multi-line cells
60 63 ip.run_cell('"""a\nb"""\n')
61 64 self.assertEqual(ip.user_ns['_'], 'a\nb')
62 65
63 66 def test_run_empty_cell(self):
64 67 """Just make sure we don't get a horrible error with a blank
65 68 cell of input. Yes, I did overlook that."""
66 69 old_xc = ip.execution_count
67 70 res = ip.run_cell('')
68 71 self.assertEqual(ip.execution_count, old_xc)
69 72 self.assertEqual(res.execution_count, None)
70 73
71 74 def test_run_cell_multiline(self):
72 75 """Multi-block, multi-line cells must execute correctly.
73 76 """
74 77 src = '\n'.join(["x=1",
75 78 "y=2",
76 79 "if 1:",
77 80 " x += 1",
78 81 " y += 1",])
79 82 res = ip.run_cell(src)
80 83 self.assertEqual(ip.user_ns['x'], 2)
81 84 self.assertEqual(ip.user_ns['y'], 3)
82 85 self.assertEqual(res.success, True)
83 86 self.assertEqual(res.result, None)
84 87
85 88 def test_multiline_string_cells(self):
86 89 "Code sprinkled with multiline strings should execute (GH-306)"
87 90 ip.run_cell('tmp=0')
88 91 self.assertEqual(ip.user_ns['tmp'], 0)
89 92 res = ip.run_cell('tmp=1;"""a\nb"""\n')
90 93 self.assertEqual(ip.user_ns['tmp'], 1)
91 94 self.assertEqual(res.success, True)
92 95 self.assertEqual(res.result, "a\nb")
93 96
94 97 def test_dont_cache_with_semicolon(self):
95 98 "Ending a line with semicolon should not cache the returned object (GH-307)"
96 99 oldlen = len(ip.user_ns['Out'])
97 100 for cell in ['1;', '1;1;']:
98 101 res = ip.run_cell(cell, store_history=True)
99 102 newlen = len(ip.user_ns['Out'])
100 103 self.assertEqual(oldlen, newlen)
101 104 self.assertIsNone(res.result)
102 105 i = 0
103 106 #also test the default caching behavior
104 107 for cell in ['1', '1;1']:
105 108 ip.run_cell(cell, store_history=True)
106 109 newlen = len(ip.user_ns['Out'])
107 110 i += 1
108 111 self.assertEqual(oldlen+i, newlen)
109 112
110 113 def test_syntax_error(self):
111 114 res = ip.run_cell("raise = 3")
112 115 self.assertIsInstance(res.error_before_exec, SyntaxError)
113 116
114 117 def test_In_variable(self):
115 118 "Verify that In variable grows with user input (GH-284)"
116 119 oldlen = len(ip.user_ns['In'])
117 120 ip.run_cell('1;', store_history=True)
118 121 newlen = len(ip.user_ns['In'])
119 122 self.assertEqual(oldlen+1, newlen)
120 123 self.assertEqual(ip.user_ns['In'][-1],'1;')
121 124
122 125 def test_magic_names_in_string(self):
123 126 ip.run_cell('a = """\n%exit\n"""')
124 127 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
125 128
126 129 def test_trailing_newline(self):
127 130 """test that running !(command) does not raise a SyntaxError"""
128 131 ip.run_cell('!(true)\n', False)
129 132 ip.run_cell('!(true)\n\n\n', False)
130 133
131 134 def test_gh_597(self):
132 135 """Pretty-printing lists of objects with non-ascii reprs may cause
133 136 problems."""
134 137 class Spam(object):
135 138 def __repr__(self):
136 139 return "\xe9"*50
137 140 import IPython.core.formatters
138 141 f = IPython.core.formatters.PlainTextFormatter()
139 142 f([Spam(),Spam()])
140 143
141 144
142 145 def test_future_flags(self):
143 146 """Check that future flags are used for parsing code (gh-777)"""
144 147 ip.run_cell('from __future__ import print_function')
145 148 try:
146 149 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
147 150 assert 'prfunc_return_val' in ip.user_ns
148 151 finally:
149 152 # Reset compiler flags so we don't mess up other tests.
150 153 ip.compile.reset_compiler_flags()
151 154
152 155 def test_future_unicode(self):
153 156 """Check that unicode_literals is imported from __future__ (gh #786)"""
154 157 try:
155 158 ip.run_cell(u'byte_str = "a"')
156 159 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
157 160 ip.run_cell('from __future__ import unicode_literals')
158 161 ip.run_cell(u'unicode_str = "a"')
159 162 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
160 163 finally:
161 164 # Reset compiler flags so we don't mess up other tests.
162 165 ip.compile.reset_compiler_flags()
163 166
164 167 def test_can_pickle(self):
165 168 "Can we pickle objects defined interactively (GH-29)"
166 169 ip = get_ipython()
167 170 ip.reset()
168 171 ip.run_cell(("class Mylist(list):\n"
169 172 " def __init__(self,x=[]):\n"
170 173 " list.__init__(self,x)"))
171 174 ip.run_cell("w=Mylist([1,2,3])")
172 175
173 176 from pickle import dumps
174 177
175 178 # We need to swap in our main module - this is only necessary
176 179 # inside the test framework, because IPython puts the interactive module
177 180 # in place (but the test framework undoes this).
178 181 _main = sys.modules['__main__']
179 182 sys.modules['__main__'] = ip.user_module
180 183 try:
181 184 res = dumps(ip.user_ns["w"])
182 185 finally:
183 186 sys.modules['__main__'] = _main
184 187 self.assertTrue(isinstance(res, bytes))
185 188
186 189 def test_global_ns(self):
187 190 "Code in functions must be able to access variables outside them."
188 191 ip = get_ipython()
189 192 ip.run_cell("a = 10")
190 193 ip.run_cell(("def f(x):\n"
191 194 " return x + a"))
192 195 ip.run_cell("b = f(12)")
193 196 self.assertEqual(ip.user_ns["b"], 22)
194 197
195 198 def test_bad_custom_tb(self):
196 199 """Check that InteractiveShell is protected from bad custom exception handlers"""
197 200 from IPython.utils import io
198 201 save_stderr = io.stderr
199 202 try:
200 203 # capture stderr
201 204 io.stderr = StringIO()
202 205 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
203 206 self.assertEqual(ip.custom_exceptions, (IOError,))
204 207 ip.run_cell(u'raise IOError("foo")')
205 208 self.assertEqual(ip.custom_exceptions, ())
206 209 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
207 210 finally:
208 211 io.stderr = save_stderr
209 212
210 213 def test_bad_custom_tb_return(self):
211 214 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
212 215 from IPython.utils import io
213 216 save_stderr = io.stderr
214 217 try:
215 218 # capture stderr
216 219 io.stderr = StringIO()
217 220 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
218 221 self.assertEqual(ip.custom_exceptions, (NameError,))
219 222 ip.run_cell(u'a=abracadabra')
220 223 self.assertEqual(ip.custom_exceptions, ())
221 224 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
222 225 finally:
223 226 io.stderr = save_stderr
224 227
225 228 def test_drop_by_id(self):
226 229 myvars = {"a":object(), "b":object(), "c": object()}
227 230 ip.push(myvars, interactive=False)
228 231 for name in myvars:
229 232 assert name in ip.user_ns, name
230 233 assert name in ip.user_ns_hidden, name
231 234 ip.user_ns['b'] = 12
232 235 ip.drop_by_id(myvars)
233 236 for name in ["a", "c"]:
234 237 assert name not in ip.user_ns, name
235 238 assert name not in ip.user_ns_hidden, name
236 239 assert ip.user_ns['b'] == 12
237 240 ip.reset()
238 241
239 242 def test_var_expand(self):
240 243 ip.user_ns['f'] = u'Ca\xf1o'
241 244 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
242 245 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
243 246 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
244 247 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
245 248
246 249 ip.user_ns['f'] = b'Ca\xc3\xb1o'
247 250 # This should not raise any exception:
248 251 ip.var_expand(u'echo $f')
249 252
250 253 def test_var_expand_local(self):
251 254 """Test local variable expansion in !system and %magic calls"""
252 255 # !system
253 256 ip.run_cell('def test():\n'
254 257 ' lvar = "ttt"\n'
255 258 ' ret = !echo {lvar}\n'
256 259 ' return ret[0]\n')
257 260 res = ip.user_ns['test']()
258 261 nt.assert_in('ttt', res)
259 262
260 263 # %magic
261 264 ip.run_cell('def makemacro():\n'
262 265 ' macroname = "macro_var_expand_locals"\n'
263 266 ' %macro {macroname} codestr\n')
264 267 ip.user_ns['codestr'] = "str(12)"
265 268 ip.run_cell('makemacro()')
266 269 nt.assert_in('macro_var_expand_locals', ip.user_ns)
267 270
268 271 def test_var_expand_self(self):
269 272 """Test variable expansion with the name 'self', which was failing.
270 273
271 274 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
272 275 """
273 276 ip.run_cell('class cTest:\n'
274 277 ' classvar="see me"\n'
275 278 ' def test(self):\n'
276 279 ' res = !echo Variable: {self.classvar}\n'
277 280 ' return res[0]\n')
278 281 nt.assert_in('see me', ip.user_ns['cTest']().test())
279 282
280 283 def test_bad_var_expand(self):
281 284 """var_expand on invalid formats shouldn't raise"""
282 285 # SyntaxError
283 286 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
284 287 # NameError
285 288 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
286 289 # ZeroDivisionError
287 290 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
288 291
289 292 def test_silent_postexec(self):
290 293 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
291 294 pre_explicit = mock.Mock()
292 295 pre_always = mock.Mock()
293 296 post_explicit = mock.Mock()
294 297 post_always = mock.Mock()
295 298
296 299 ip.events.register('pre_run_cell', pre_explicit)
297 300 ip.events.register('pre_execute', pre_always)
298 301 ip.events.register('post_run_cell', post_explicit)
299 302 ip.events.register('post_execute', post_always)
300 303
301 304 try:
302 305 ip.run_cell("1", silent=True)
303 306 assert pre_always.called
304 307 assert not pre_explicit.called
305 308 assert post_always.called
306 309 assert not post_explicit.called
307 310 # double-check that non-silent exec did what we expected
308 311 # silent to avoid
309 312 ip.run_cell("1")
310 313 assert pre_explicit.called
311 314 assert post_explicit.called
312 315 finally:
313 316 # remove post-exec
314 317 ip.events.unregister('pre_run_cell', pre_explicit)
315 318 ip.events.unregister('pre_execute', pre_always)
316 319 ip.events.unregister('post_run_cell', post_explicit)
317 320 ip.events.unregister('post_execute', post_always)
318 321
319 322 def test_silent_noadvance(self):
320 323 """run_cell(silent=True) doesn't advance execution_count"""
321 324 ec = ip.execution_count
322 325 # silent should force store_history=False
323 326 ip.run_cell("1", store_history=True, silent=True)
324 327
325 328 self.assertEqual(ec, ip.execution_count)
326 329 # double-check that non-silent exec did what we expected
327 330 # silent to avoid
328 331 ip.run_cell("1", store_history=True)
329 332 self.assertEqual(ec+1, ip.execution_count)
330 333
331 334 def test_silent_nodisplayhook(self):
332 335 """run_cell(silent=True) doesn't trigger displayhook"""
333 336 d = dict(called=False)
334 337
335 338 trap = ip.display_trap
336 339 save_hook = trap.hook
337 340
338 341 def failing_hook(*args, **kwargs):
339 342 d['called'] = True
340 343
341 344 try:
342 345 trap.hook = failing_hook
343 346 res = ip.run_cell("1", silent=True)
344 347 self.assertFalse(d['called'])
345 348 self.assertIsNone(res.result)
346 349 # double-check that non-silent exec did what we expected
347 350 # silent to avoid
348 351 ip.run_cell("1")
349 352 self.assertTrue(d['called'])
350 353 finally:
351 354 trap.hook = save_hook
352 355
353 356 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
354 357 def test_print_softspace(self):
355 358 """Verify that softspace is handled correctly when executing multiple
356 359 statements.
357 360
358 361 In [1]: print 1; print 2
359 362 1
360 363 2
361 364
362 365 In [2]: print 1,; print 2
363 366 1 2
364 367 """
365 368
366 369 def test_ofind_line_magic(self):
367 370 from IPython.core.magic import register_line_magic
368 371
369 372 @register_line_magic
370 373 def lmagic(line):
371 374 "A line magic"
372 375
373 376 # Get info on line magic
374 377 lfind = ip._ofind('lmagic')
375 378 info = dict(found=True, isalias=False, ismagic=True,
376 379 namespace = 'IPython internal', obj= lmagic.__wrapped__,
377 380 parent = None)
378 381 nt.assert_equal(lfind, info)
379 382
380 383 def test_ofind_cell_magic(self):
381 384 from IPython.core.magic import register_cell_magic
382 385
383 386 @register_cell_magic
384 387 def cmagic(line, cell):
385 388 "A cell magic"
386 389
387 390 # Get info on cell magic
388 391 find = ip._ofind('cmagic')
389 392 info = dict(found=True, isalias=False, ismagic=True,
390 393 namespace = 'IPython internal', obj= cmagic.__wrapped__,
391 394 parent = None)
392 395 nt.assert_equal(find, info)
393 396
394 397 def test_ofind_property_with_error(self):
395 398 class A(object):
396 399 @property
397 400 def foo(self):
398 401 raise NotImplementedError()
399 402 a = A()
400 403
401 404 found = ip._ofind('a.foo', [('locals', locals())])
402 405 info = dict(found=True, isalias=False, ismagic=False,
403 406 namespace='locals', obj=A.foo, parent=a)
404 407 nt.assert_equal(found, info)
405 408
406 409 def test_ofind_multiple_attribute_lookups(self):
407 410 class A(object):
408 411 @property
409 412 def foo(self):
410 413 raise NotImplementedError()
411 414
412 415 a = A()
413 416 a.a = A()
414 417 a.a.a = A()
415 418
416 419 found = ip._ofind('a.a.a.foo', [('locals', locals())])
417 420 info = dict(found=True, isalias=False, ismagic=False,
418 421 namespace='locals', obj=A.foo, parent=a.a.a)
419 422 nt.assert_equal(found, info)
420 423
421 424 def test_ofind_slotted_attributes(self):
422 425 class A(object):
423 426 __slots__ = ['foo']
424 427 def __init__(self):
425 428 self.foo = 'bar'
426 429
427 430 a = A()
428 431 found = ip._ofind('a.foo', [('locals', locals())])
429 432 info = dict(found=True, isalias=False, ismagic=False,
430 433 namespace='locals', obj=a.foo, parent=a)
431 434 nt.assert_equal(found, info)
432 435
433 436 found = ip._ofind('a.bar', [('locals', locals())])
434 437 info = dict(found=False, isalias=False, ismagic=False,
435 438 namespace=None, obj=None, parent=a)
436 439 nt.assert_equal(found, info)
437 440
438 441 def test_ofind_prefers_property_to_instance_level_attribute(self):
439 442 class A(object):
440 443 @property
441 444 def foo(self):
442 445 return 'bar'
443 446 a = A()
444 447 a.__dict__['foo'] = 'baz'
445 448 nt.assert_equal(a.foo, 'bar')
446 449 found = ip._ofind('a.foo', [('locals', locals())])
447 450 nt.assert_is(found['obj'], A.foo)
448 451
449 452 def test_custom_exception(self):
450 453 called = []
451 454 def my_handler(shell, etype, value, tb, tb_offset=None):
452 455 called.append(etype)
453 456 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
454 457
455 458 ip.set_custom_exc((ValueError,), my_handler)
456 459 try:
457 460 res = ip.run_cell("raise ValueError('test')")
458 461 # Check that this was called, and only once.
459 462 self.assertEqual(called, [ValueError])
460 463 # Check that the error is on the result object
461 464 self.assertIsInstance(res.error_in_exec, ValueError)
462 465 finally:
463 466 # Reset the custom exception hook
464 467 ip.set_custom_exc((), None)
465 468
466 469 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
467 470 def test_future_environment(self):
468 471 "Can we run code with & without the shell's __future__ imports?"
469 472 ip.run_cell("from __future__ import division")
470 473 ip.run_cell("a = 1/2", shell_futures=True)
471 474 self.assertEqual(ip.user_ns['a'], 0.5)
472 475 ip.run_cell("b = 1/2", shell_futures=False)
473 476 self.assertEqual(ip.user_ns['b'], 0)
474 477
475 478 ip.compile.reset_compiler_flags()
476 479 # This shouldn't leak to the shell's compiler
477 480 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
478 481 self.assertEqual(ip.user_ns['c'], 0.5)
479 482 ip.run_cell("d = 1/2", shell_futures=True)
480 483 self.assertEqual(ip.user_ns['d'], 0)
481 484
482 485 def test_mktempfile(self):
483 486 filename = ip.mktempfile()
484 487 # Check that we can open the file again on Windows
485 488 with open(filename, 'w') as f:
486 489 f.write('abc')
487 490
488 491 filename = ip.mktempfile(data='blah')
489 492 with open(filename, 'r') as f:
490 493 self.assertEqual(f.read(), 'blah')
491 494
492 495 def test_new_main_mod(self):
493 496 # Smoketest to check that this accepts a unicode module name
494 497 name = u'jiefmw'
495 498 mod = ip.new_main_mod(u'%s.py' % name, name)
496 499 self.assertEqual(mod.__name__, name)
497 500
498 501 def test_get_exception_only(self):
499 502 try:
500 503 raise KeyboardInterrupt
501 504 except KeyboardInterrupt:
502 505 msg = ip.get_exception_only()
503 506 self.assertEqual(msg, 'KeyboardInterrupt\n')
504 507
505 class DerivedInterrupt(KeyboardInterrupt):
506 pass
507 508 try:
508 509 raise DerivedInterrupt("foo")
509 510 except KeyboardInterrupt:
510 511 msg = ip.get_exception_only()
511 512 if sys.version_info[0] <= 2:
512 513 self.assertEqual(msg, 'DerivedInterrupt: foo\n')
513 514 else:
514 515 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
515 516
516 517 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
517 518
518 519 @onlyif_unicode_paths
519 520 def setUp(self):
520 521 self.BASETESTDIR = tempfile.mkdtemp()
521 522 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
522 523 os.mkdir(self.TESTDIR)
523 524 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
524 525 sfile.write("pass\n")
525 526 self.oldpath = py3compat.getcwd()
526 527 os.chdir(self.TESTDIR)
527 528 self.fname = u"Γ₯Àâtestscript.py"
528 529
529 530 def tearDown(self):
530 531 os.chdir(self.oldpath)
531 532 shutil.rmtree(self.BASETESTDIR)
532 533
533 534 @onlyif_unicode_paths
534 535 def test_1(self):
535 536 """Test safe_execfile with non-ascii path
536 537 """
537 538 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
538 539
539 540 class ExitCodeChecks(tt.TempFileMixin):
540 541 def test_exit_code_ok(self):
541 542 self.system('exit 0')
542 543 self.assertEqual(ip.user_ns['_exit_code'], 0)
543 544
544 545 def test_exit_code_error(self):
545 546 self.system('exit 1')
546 547 self.assertEqual(ip.user_ns['_exit_code'], 1)
547 548
548 549 @skipif(not hasattr(signal, 'SIGALRM'))
549 550 def test_exit_code_signal(self):
550 551 self.mktmp("import signal, time\n"
551 552 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
552 553 "time.sleep(1)\n")
553 554 self.system("%s %s" % (sys.executable, self.fname))
554 555 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
555 556
556 557 @onlyif_cmds_exist("csh")
557 558 def test_exit_code_signal_csh(self):
558 559 SHELL = os.environ.get('SHELL', None)
559 560 os.environ['SHELL'] = find_cmd("csh")
560 561 try:
561 562 self.test_exit_code_signal()
562 563 finally:
563 564 if SHELL is not None:
564 565 os.environ['SHELL'] = SHELL
565 566 else:
566 567 del os.environ['SHELL']
567 568
568 569 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
569 570 system = ip.system_raw
570 571
571 572 @onlyif_unicode_paths
572 573 def test_1(self):
573 574 """Test system_raw with non-ascii cmd
574 575 """
575 576 cmd = u'''python -c "'Γ₯Àâ'" '''
576 577 ip.system_raw(cmd)
577 578
578 579 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
579 580 @mock.patch('os.system', side_effect=KeyboardInterrupt)
580 581 def test_control_c(self, *mocks):
581 582 try:
582 583 self.system("sleep 1 # wont happen")
583 584 except KeyboardInterrupt:
584 585 self.fail("system call should intercept "
585 586 "keyboard interrupt from subprocess.call")
586 587 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
587 588
588 589 # TODO: Exit codes are currently ignored on Windows.
589 590 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
590 591 system = ip.system_piped
591 592
592 593 @skip_win32
593 594 def test_exit_code_ok(self):
594 595 ExitCodeChecks.test_exit_code_ok(self)
595 596
596 597 @skip_win32
597 598 def test_exit_code_error(self):
598 599 ExitCodeChecks.test_exit_code_error(self)
599 600
600 601 @skip_win32
601 602 def test_exit_code_signal(self):
602 603 ExitCodeChecks.test_exit_code_signal(self)
603 604
604 605 class TestModules(unittest.TestCase, tt.TempFileMixin):
605 606 def test_extraneous_loads(self):
606 607 """Test we're not loading modules on startup that we shouldn't.
607 608 """
608 609 self.mktmp("import sys\n"
609 610 "print('numpy' in sys.modules)\n"
610 611 "print('ipython_parallel' in sys.modules)\n"
611 612 "print('ipython_kernel' in sys.modules)\n"
612 613 )
613 614 out = "False\nFalse\nFalse\n"
614 615 tt.ipexec_validate(self.fname, out)
615 616
616 617 class Negator(ast.NodeTransformer):
617 618 """Negates all number literals in an AST."""
618 619 def visit_Num(self, node):
619 620 node.n = -node.n
620 621 return node
621 622
622 623 class TestAstTransform(unittest.TestCase):
623 624 def setUp(self):
624 625 self.negator = Negator()
625 626 ip.ast_transformers.append(self.negator)
626 627
627 628 def tearDown(self):
628 629 ip.ast_transformers.remove(self.negator)
629 630
630 631 def test_run_cell(self):
631 632 with tt.AssertPrints('-34'):
632 633 ip.run_cell('print (12 + 22)')
633 634
634 635 # A named reference to a number shouldn't be transformed.
635 636 ip.user_ns['n'] = 55
636 637 with tt.AssertNotPrints('-55'):
637 638 ip.run_cell('print (n)')
638 639
639 640 def test_timeit(self):
640 641 called = set()
641 642 def f(x):
642 643 called.add(x)
643 644 ip.push({'f':f})
644 645
645 646 with tt.AssertPrints("best of "):
646 647 ip.run_line_magic("timeit", "-n1 f(1)")
647 648 self.assertEqual(called, set([-1]))
648 649 called.clear()
649 650
650 651 with tt.AssertPrints("best of "):
651 652 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
652 653 self.assertEqual(called, set([-2, -3]))
653 654
654 655 def test_time(self):
655 656 called = []
656 657 def f(x):
657 658 called.append(x)
658 659 ip.push({'f':f})
659 660
660 661 # Test with an expression
661 662 with tt.AssertPrints("Wall time: "):
662 663 ip.run_line_magic("time", "f(5+9)")
663 664 self.assertEqual(called, [-14])
664 665 called[:] = []
665 666
666 667 # Test with a statement (different code path)
667 668 with tt.AssertPrints("Wall time: "):
668 669 ip.run_line_magic("time", "a = f(-3 + -2)")
669 670 self.assertEqual(called, [5])
670 671
671 672 def test_macro(self):
672 673 ip.push({'a':10})
673 674 # The AST transformation makes this do a+=-1
674 675 ip.define_macro("amacro", "a+=1\nprint(a)")
675 676
676 677 with tt.AssertPrints("9"):
677 678 ip.run_cell("amacro")
678 679 with tt.AssertPrints("8"):
679 680 ip.run_cell("amacro")
680 681
681 682 class IntegerWrapper(ast.NodeTransformer):
682 683 """Wraps all integers in a call to Integer()"""
683 684 def visit_Num(self, node):
684 685 if isinstance(node.n, int):
685 686 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
686 687 args=[node], keywords=[])
687 688 return node
688 689
689 690 class TestAstTransform2(unittest.TestCase):
690 691 def setUp(self):
691 692 self.intwrapper = IntegerWrapper()
692 693 ip.ast_transformers.append(self.intwrapper)
693 694
694 695 self.calls = []
695 696 def Integer(*args):
696 697 self.calls.append(args)
697 698 return args
698 699 ip.push({"Integer": Integer})
699 700
700 701 def tearDown(self):
701 702 ip.ast_transformers.remove(self.intwrapper)
702 703 del ip.user_ns['Integer']
703 704
704 705 def test_run_cell(self):
705 706 ip.run_cell("n = 2")
706 707 self.assertEqual(self.calls, [(2,)])
707 708
708 709 # This shouldn't throw an error
709 710 ip.run_cell("o = 2.0")
710 711 self.assertEqual(ip.user_ns['o'], 2.0)
711 712
712 713 def test_timeit(self):
713 714 called = set()
714 715 def f(x):
715 716 called.add(x)
716 717 ip.push({'f':f})
717 718
718 719 with tt.AssertPrints("best of "):
719 720 ip.run_line_magic("timeit", "-n1 f(1)")
720 721 self.assertEqual(called, set([(1,)]))
721 722 called.clear()
722 723
723 724 with tt.AssertPrints("best of "):
724 725 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
725 726 self.assertEqual(called, set([(2,), (3,)]))
726 727
727 728 class ErrorTransformer(ast.NodeTransformer):
728 729 """Throws an error when it sees a number."""
729 730 def visit_Num(self, node):
730 731 raise ValueError("test")
731 732
732 733 class TestAstTransformError(unittest.TestCase):
733 734 def test_unregistering(self):
734 735 err_transformer = ErrorTransformer()
735 736 ip.ast_transformers.append(err_transformer)
736 737
737 738 with tt.AssertPrints("unregister", channel='stderr'):
738 739 ip.run_cell("1 + 2")
739 740
740 741 # This should have been removed.
741 742 nt.assert_not_in(err_transformer, ip.ast_transformers)
742 743
743 744
744 745 class StringRejector(ast.NodeTransformer):
745 746 """Throws an InputRejected when it sees a string literal.
746 747
747 748 Used to verify that NodeTransformers can signal that a piece of code should
748 749 not be executed by throwing an InputRejected.
749 750 """
750 751
751 752 def visit_Str(self, node):
752 753 raise InputRejected("test")
753 754
754 755
755 756 class TestAstTransformInputRejection(unittest.TestCase):
756 757
757 758 def setUp(self):
758 759 self.transformer = StringRejector()
759 760 ip.ast_transformers.append(self.transformer)
760 761
761 762 def tearDown(self):
762 763 ip.ast_transformers.remove(self.transformer)
763 764
764 765 def test_input_rejection(self):
765 766 """Check that NodeTransformers can reject input."""
766 767
767 768 expect_exception_tb = tt.AssertPrints("InputRejected: test")
768 769 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
769 770
770 771 # Run the same check twice to verify that the transformer is not
771 772 # disabled after raising.
772 773 with expect_exception_tb, expect_no_cell_output:
773 774 ip.run_cell("'unsafe'")
774 775
775 776 with expect_exception_tb, expect_no_cell_output:
776 777 res = ip.run_cell("'unsafe'")
777 778
778 779 self.assertIsInstance(res.error_before_exec, InputRejected)
779 780
780 781 def test__IPYTHON__():
781 782 # This shouldn't raise a NameError, that's all
782 783 __IPYTHON__
783 784
784 785
785 786 class DummyRepr(object):
786 787 def __repr__(self):
787 788 return "DummyRepr"
788 789
789 790 def _repr_html_(self):
790 791 return "<b>dummy</b>"
791 792
792 793 def _repr_javascript_(self):
793 794 return "console.log('hi');", {'key': 'value'}
794 795
795 796
796 797 def test_user_variables():
797 798 # enable all formatters
798 799 ip.display_formatter.active_types = ip.display_formatter.format_types
799 800
800 801 ip.user_ns['dummy'] = d = DummyRepr()
801 802 keys = set(['dummy', 'doesnotexist'])
802 803 r = ip.user_expressions({ key:key for key in keys})
803 804
804 805 nt.assert_equal(keys, set(r.keys()))
805 806 dummy = r['dummy']
806 807 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
807 808 nt.assert_equal(dummy['status'], 'ok')
808 809 data = dummy['data']
809 810 metadata = dummy['metadata']
810 811 nt.assert_equal(data.get('text/html'), d._repr_html_())
811 812 js, jsmd = d._repr_javascript_()
812 813 nt.assert_equal(data.get('application/javascript'), js)
813 814 nt.assert_equal(metadata.get('application/javascript'), jsmd)
814 815
815 816 dne = r['doesnotexist']
816 817 nt.assert_equal(dne['status'], 'error')
817 818 nt.assert_equal(dne['ename'], 'NameError')
818 819
819 820 # back to text only
820 821 ip.display_formatter.active_types = ['text/plain']
821 822
822 823 def test_user_expression():
823 824 # enable all formatters
824 825 ip.display_formatter.active_types = ip.display_formatter.format_types
825 826 query = {
826 827 'a' : '1 + 2',
827 828 'b' : '1/0',
828 829 }
829 830 r = ip.user_expressions(query)
830 831 import pprint
831 832 pprint.pprint(r)
832 833 nt.assert_equal(set(r.keys()), set(query.keys()))
833 834 a = r['a']
834 835 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
835 836 nt.assert_equal(a['status'], 'ok')
836 837 data = a['data']
837 838 metadata = a['metadata']
838 839 nt.assert_equal(data.get('text/plain'), '3')
839 840
840 841 b = r['b']
841 842 nt.assert_equal(b['status'], 'error')
842 843 nt.assert_equal(b['ename'], 'ZeroDivisionError')
843 844
844 845 # back to text only
845 846 ip.display_formatter.active_types = ['text/plain']
846 847
847 848
848 849
849 850
850 851
851 852 class TestSyntaxErrorTransformer(unittest.TestCase):
852 853 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
853 854
854 855 class SyntaxErrorTransformer(InputTransformer):
855 856
856 857 def push(self, line):
857 858 pos = line.find('syntaxerror')
858 859 if pos >= 0:
859 860 e = SyntaxError('input contains "syntaxerror"')
860 861 e.text = line
861 862 e.offset = pos + 1
862 863 raise e
863 864 return line
864 865
865 866 def reset(self):
866 867 pass
867 868
868 869 def setUp(self):
869 870 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
870 871 ip.input_splitter.python_line_transforms.append(self.transformer)
871 872 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
872 873
873 874 def tearDown(self):
874 875 ip.input_splitter.python_line_transforms.remove(self.transformer)
875 876 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
876 877
877 878 def test_syntaxerror_input_transformer(self):
878 879 with tt.AssertPrints('1234'):
879 880 ip.run_cell('1234')
880 881 with tt.AssertPrints('SyntaxError: invalid syntax'):
881 882 ip.run_cell('1 2 3') # plain python syntax error
882 883 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
883 884 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
884 885 with tt.AssertPrints('3456'):
885 886 ip.run_cell('3456')
886 887
887 888
888 889
889 890 def test_warning_suppression():
890 891 ip.run_cell("import warnings")
891 892 try:
892 893 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
893 894 ip.run_cell("warnings.warn('asdf')")
894 895 # Here's the real test -- if we run that again, we should get the
895 896 # warning again. Traditionally, each warning was only issued once per
896 897 # IPython session (approximately), even if the user typed in new and
897 898 # different code that should have also triggered the warning, leading
898 899 # to much confusion.
899 900 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
900 901 ip.run_cell("warnings.warn('asdf')")
901 902 finally:
902 903 ip.run_cell("del warnings")
General Comments 0
You need to be logged in to leave comments. Login now