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