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