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