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