##// END OF EJS Templates
Adding test for magic warnings
Sammy Al Hashemi -
Show More
@@ -1,1061 +1,1068 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 452 @mock.patch("builtins.print")
453 453 def test_showtraceback_with_surrogates(self, mocked_print):
454 454 values = []
455 455
456 456 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
457 457 values.append(value)
458 458 if value == chr(0xD8FF):
459 459 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
460 460
461 461 # mock builtins.print
462 462 mocked_print.side_effect = mock_print_func
463 463
464 464 # ip._showtraceback() is replaced in globalipapp.py.
465 465 # Call original method to test.
466 466 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
467 467
468 468 self.assertEqual(mocked_print.call_count, 2)
469 469 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
470 470
471 471 def test_mktempfile(self):
472 472 filename = ip.mktempfile()
473 473 # Check that we can open the file again on Windows
474 474 with open(filename, 'w') as f:
475 475 f.write('abc')
476 476
477 477 filename = ip.mktempfile(data='blah')
478 478 with open(filename, 'r') as f:
479 479 self.assertEqual(f.read(), 'blah')
480 480
481 481 def test_new_main_mod(self):
482 482 # Smoketest to check that this accepts a unicode module name
483 483 name = u'jiefmw'
484 484 mod = ip.new_main_mod(u'%s.py' % name, name)
485 485 self.assertEqual(mod.__name__, name)
486 486
487 487 def test_get_exception_only(self):
488 488 try:
489 489 raise KeyboardInterrupt
490 490 except KeyboardInterrupt:
491 491 msg = ip.get_exception_only()
492 492 self.assertEqual(msg, 'KeyboardInterrupt\n')
493 493
494 494 try:
495 495 raise DerivedInterrupt("foo")
496 496 except KeyboardInterrupt:
497 497 msg = ip.get_exception_only()
498 498 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
499 499
500 500 def test_inspect_text(self):
501 501 ip.run_cell('a = 5')
502 502 text = ip.object_inspect_text('a')
503 503 self.assertIsInstance(text, str)
504 504
505 505 def test_last_execution_result(self):
506 506 """ Check that last execution result gets set correctly (GH-10702) """
507 507 result = ip.run_cell('a = 5; a')
508 508 self.assertTrue(ip.last_execution_succeeded)
509 509 self.assertEqual(ip.last_execution_result.result, 5)
510 510
511 511 result = ip.run_cell('a = x_invalid_id_x')
512 512 self.assertFalse(ip.last_execution_succeeded)
513 513 self.assertFalse(ip.last_execution_result.success)
514 514 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
515 515
516 516 def test_reset_aliasing(self):
517 517 """ Check that standard posix aliases work after %reset. """
518 518 if os.name != 'posix':
519 519 return
520 520
521 521 ip.reset()
522 522 for cmd in ('clear', 'more', 'less', 'man'):
523 523 res = ip.run_cell('%' + cmd)
524 524 self.assertEqual(res.success, True)
525 525
526 526
527 527 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
528 528
529 529 @onlyif_unicode_paths
530 530 def setUp(self):
531 531 self.BASETESTDIR = tempfile.mkdtemp()
532 532 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
533 533 os.mkdir(self.TESTDIR)
534 534 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
535 535 sfile.write("pass\n")
536 536 self.oldpath = os.getcwd()
537 537 os.chdir(self.TESTDIR)
538 538 self.fname = u"Γ₯Àâtestscript.py"
539 539
540 540 def tearDown(self):
541 541 os.chdir(self.oldpath)
542 542 shutil.rmtree(self.BASETESTDIR)
543 543
544 544 @onlyif_unicode_paths
545 545 def test_1(self):
546 546 """Test safe_execfile with non-ascii path
547 547 """
548 548 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
549 549
550 550 class ExitCodeChecks(tt.TempFileMixin):
551 551
552 552 def setUp(self):
553 553 self.system = ip.system_raw
554 554
555 555 def test_exit_code_ok(self):
556 556 self.system('exit 0')
557 557 self.assertEqual(ip.user_ns['_exit_code'], 0)
558 558
559 559 def test_exit_code_error(self):
560 560 self.system('exit 1')
561 561 self.assertEqual(ip.user_ns['_exit_code'], 1)
562 562
563 563 @skipif(not hasattr(signal, 'SIGALRM'))
564 564 def test_exit_code_signal(self):
565 565 self.mktmp("import signal, time\n"
566 566 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
567 567 "time.sleep(1)\n")
568 568 self.system("%s %s" % (sys.executable, self.fname))
569 569 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
570 570
571 571 @onlyif_cmds_exist("csh")
572 572 def test_exit_code_signal_csh(self):
573 573 SHELL = os.environ.get('SHELL', None)
574 574 os.environ['SHELL'] = find_cmd("csh")
575 575 try:
576 576 self.test_exit_code_signal()
577 577 finally:
578 578 if SHELL is not None:
579 579 os.environ['SHELL'] = SHELL
580 580 else:
581 581 del os.environ['SHELL']
582 582
583 583
584 584 class TestSystemRaw(ExitCodeChecks):
585 585
586 586 def setUp(self):
587 587 super().setUp()
588 588 self.system = ip.system_raw
589 589
590 590 @onlyif_unicode_paths
591 591 def test_1(self):
592 592 """Test system_raw with non-ascii cmd
593 593 """
594 594 cmd = u'''python -c "'Γ₯Àâ'" '''
595 595 ip.system_raw(cmd)
596 596
597 597 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
598 598 @mock.patch('os.system', side_effect=KeyboardInterrupt)
599 599 def test_control_c(self, *mocks):
600 600 try:
601 601 self.system("sleep 1 # wont happen")
602 602 except KeyboardInterrupt:
603 self.fail("system call should intercept "
604 "keyboard interrupt from subprocess.call")
605 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
603 self.fail(
604 "system call should intercept "
605 "keyboard interrupt from subprocess.call"
606 )
607 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
608
609 def test_magic_warnings(self):
610 for magic_cmd in ("ls", "pip", "conda", "cd"):
611 with self.assertWarnsRegex(Warning, "You executed the system command"):
612 ip.system_raw(magic_cmd)
606 613
607 614 # TODO: Exit codes are currently ignored on Windows.
608 615 class TestSystemPipedExitCode(ExitCodeChecks):
609 616
610 617 def setUp(self):
611 618 super().setUp()
612 619 self.system = ip.system_piped
613 620
614 621 @skip_win32
615 622 def test_exit_code_ok(self):
616 623 ExitCodeChecks.test_exit_code_ok(self)
617 624
618 625 @skip_win32
619 626 def test_exit_code_error(self):
620 627 ExitCodeChecks.test_exit_code_error(self)
621 628
622 629 @skip_win32
623 630 def test_exit_code_signal(self):
624 631 ExitCodeChecks.test_exit_code_signal(self)
625 632
626 633 class TestModules(tt.TempFileMixin):
627 634 def test_extraneous_loads(self):
628 635 """Test we're not loading modules on startup that we shouldn't.
629 636 """
630 637 self.mktmp("import sys\n"
631 638 "print('numpy' in sys.modules)\n"
632 639 "print('ipyparallel' in sys.modules)\n"
633 640 "print('ipykernel' in sys.modules)\n"
634 641 )
635 642 out = "False\nFalse\nFalse\n"
636 643 tt.ipexec_validate(self.fname, out)
637 644
638 645 class Negator(ast.NodeTransformer):
639 646 """Negates all number literals in an AST."""
640 647
641 648 # for python 3.7 and earlier
642 649 def visit_Num(self, node):
643 650 node.n = -node.n
644 651 return node
645 652
646 653 # for python 3.8+
647 654 def visit_Constant(self, node):
648 655 if isinstance(node.value, int):
649 656 return self.visit_Num(node)
650 657 return node
651 658
652 659 class TestAstTransform(unittest.TestCase):
653 660 def setUp(self):
654 661 self.negator = Negator()
655 662 ip.ast_transformers.append(self.negator)
656 663
657 664 def tearDown(self):
658 665 ip.ast_transformers.remove(self.negator)
659 666
660 667 def test_run_cell(self):
661 668 with tt.AssertPrints('-34'):
662 669 ip.run_cell('print (12 + 22)')
663 670
664 671 # A named reference to a number shouldn't be transformed.
665 672 ip.user_ns['n'] = 55
666 673 with tt.AssertNotPrints('-55'):
667 674 ip.run_cell('print (n)')
668 675
669 676 def test_timeit(self):
670 677 called = set()
671 678 def f(x):
672 679 called.add(x)
673 680 ip.push({'f':f})
674 681
675 682 with tt.AssertPrints("std. dev. of"):
676 683 ip.run_line_magic("timeit", "-n1 f(1)")
677 684 self.assertEqual(called, {-1})
678 685 called.clear()
679 686
680 687 with tt.AssertPrints("std. dev. of"):
681 688 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
682 689 self.assertEqual(called, {-2, -3})
683 690
684 691 def test_time(self):
685 692 called = []
686 693 def f(x):
687 694 called.append(x)
688 695 ip.push({'f':f})
689 696
690 697 # Test with an expression
691 698 with tt.AssertPrints("Wall time: "):
692 699 ip.run_line_magic("time", "f(5+9)")
693 700 self.assertEqual(called, [-14])
694 701 called[:] = []
695 702
696 703 # Test with a statement (different code path)
697 704 with tt.AssertPrints("Wall time: "):
698 705 ip.run_line_magic("time", "a = f(-3 + -2)")
699 706 self.assertEqual(called, [5])
700 707
701 708 def test_macro(self):
702 709 ip.push({'a':10})
703 710 # The AST transformation makes this do a+=-1
704 711 ip.define_macro("amacro", "a+=1\nprint(a)")
705 712
706 713 with tt.AssertPrints("9"):
707 714 ip.run_cell("amacro")
708 715 with tt.AssertPrints("8"):
709 716 ip.run_cell("amacro")
710 717
711 718 class TestMiscTransform(unittest.TestCase):
712 719
713 720
714 721 def test_transform_only_once(self):
715 722 cleanup = 0
716 723 line_t = 0
717 724 def count_cleanup(lines):
718 725 nonlocal cleanup
719 726 cleanup += 1
720 727 return lines
721 728
722 729 def count_line_t(lines):
723 730 nonlocal line_t
724 731 line_t += 1
725 732 return lines
726 733
727 734 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
728 735 ip.input_transformer_manager.line_transforms.append(count_line_t)
729 736
730 737 ip.run_cell('1')
731 738
732 739 assert cleanup == 1
733 740 assert line_t == 1
734 741
735 742 class IntegerWrapper(ast.NodeTransformer):
736 743 """Wraps all integers in a call to Integer()"""
737 744
738 745 # for Python 3.7 and earlier
739 746
740 747 # for Python 3.7 and earlier
741 748 def visit_Num(self, node):
742 749 if isinstance(node.n, int):
743 750 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
744 751 args=[node], keywords=[])
745 752 return node
746 753
747 754 # For Python 3.8+
748 755 def visit_Constant(self, node):
749 756 if isinstance(node.value, int):
750 757 return self.visit_Num(node)
751 758 return node
752 759
753 760
754 761 class TestAstTransform2(unittest.TestCase):
755 762 def setUp(self):
756 763 self.intwrapper = IntegerWrapper()
757 764 ip.ast_transformers.append(self.intwrapper)
758 765
759 766 self.calls = []
760 767 def Integer(*args):
761 768 self.calls.append(args)
762 769 return args
763 770 ip.push({"Integer": Integer})
764 771
765 772 def tearDown(self):
766 773 ip.ast_transformers.remove(self.intwrapper)
767 774 del ip.user_ns['Integer']
768 775
769 776 def test_run_cell(self):
770 777 ip.run_cell("n = 2")
771 778 self.assertEqual(self.calls, [(2,)])
772 779
773 780 # This shouldn't throw an error
774 781 ip.run_cell("o = 2.0")
775 782 self.assertEqual(ip.user_ns['o'], 2.0)
776 783
777 784 def test_timeit(self):
778 785 called = set()
779 786 def f(x):
780 787 called.add(x)
781 788 ip.push({'f':f})
782 789
783 790 with tt.AssertPrints("std. dev. of"):
784 791 ip.run_line_magic("timeit", "-n1 f(1)")
785 792 self.assertEqual(called, {(1,)})
786 793 called.clear()
787 794
788 795 with tt.AssertPrints("std. dev. of"):
789 796 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
790 797 self.assertEqual(called, {(2,), (3,)})
791 798
792 799 class ErrorTransformer(ast.NodeTransformer):
793 800 """Throws an error when it sees a number."""
794 801
795 802 # for Python 3.7 and earlier
796 803 def visit_Num(self, node):
797 804 raise ValueError("test")
798 805
799 806 # for Python 3.8+
800 807 def visit_Constant(self, node):
801 808 if isinstance(node.value, int):
802 809 return self.visit_Num(node)
803 810 return node
804 811
805 812
806 813 class TestAstTransformError(unittest.TestCase):
807 814 def test_unregistering(self):
808 815 err_transformer = ErrorTransformer()
809 816 ip.ast_transformers.append(err_transformer)
810 817
811 818 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
812 819 ip.run_cell("1 + 2")
813 820
814 821 # This should have been removed.
815 822 nt.assert_not_in(err_transformer, ip.ast_transformers)
816 823
817 824
818 825 class StringRejector(ast.NodeTransformer):
819 826 """Throws an InputRejected when it sees a string literal.
820 827
821 828 Used to verify that NodeTransformers can signal that a piece of code should
822 829 not be executed by throwing an InputRejected.
823 830 """
824 831
825 832 #for python 3.7 and earlier
826 833 def visit_Str(self, node):
827 834 raise InputRejected("test")
828 835
829 836 # 3.8 only
830 837 def visit_Constant(self, node):
831 838 if isinstance(node.value, str):
832 839 raise InputRejected("test")
833 840 return node
834 841
835 842
836 843 class TestAstTransformInputRejection(unittest.TestCase):
837 844
838 845 def setUp(self):
839 846 self.transformer = StringRejector()
840 847 ip.ast_transformers.append(self.transformer)
841 848
842 849 def tearDown(self):
843 850 ip.ast_transformers.remove(self.transformer)
844 851
845 852 def test_input_rejection(self):
846 853 """Check that NodeTransformers can reject input."""
847 854
848 855 expect_exception_tb = tt.AssertPrints("InputRejected: test")
849 856 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
850 857
851 858 # Run the same check twice to verify that the transformer is not
852 859 # disabled after raising.
853 860 with expect_exception_tb, expect_no_cell_output:
854 861 ip.run_cell("'unsafe'")
855 862
856 863 with expect_exception_tb, expect_no_cell_output:
857 864 res = ip.run_cell("'unsafe'")
858 865
859 866 self.assertIsInstance(res.error_before_exec, InputRejected)
860 867
861 868 def test__IPYTHON__():
862 869 # This shouldn't raise a NameError, that's all
863 870 __IPYTHON__
864 871
865 872
866 873 class DummyRepr(object):
867 874 def __repr__(self):
868 875 return "DummyRepr"
869 876
870 877 def _repr_html_(self):
871 878 return "<b>dummy</b>"
872 879
873 880 def _repr_javascript_(self):
874 881 return "console.log('hi');", {'key': 'value'}
875 882
876 883
877 884 def test_user_variables():
878 885 # enable all formatters
879 886 ip.display_formatter.active_types = ip.display_formatter.format_types
880 887
881 888 ip.user_ns['dummy'] = d = DummyRepr()
882 889 keys = {'dummy', 'doesnotexist'}
883 890 r = ip.user_expressions({ key:key for key in keys})
884 891
885 892 nt.assert_equal(keys, set(r.keys()))
886 893 dummy = r['dummy']
887 894 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
888 895 nt.assert_equal(dummy['status'], 'ok')
889 896 data = dummy['data']
890 897 metadata = dummy['metadata']
891 898 nt.assert_equal(data.get('text/html'), d._repr_html_())
892 899 js, jsmd = d._repr_javascript_()
893 900 nt.assert_equal(data.get('application/javascript'), js)
894 901 nt.assert_equal(metadata.get('application/javascript'), jsmd)
895 902
896 903 dne = r['doesnotexist']
897 904 nt.assert_equal(dne['status'], 'error')
898 905 nt.assert_equal(dne['ename'], 'NameError')
899 906
900 907 # back to text only
901 908 ip.display_formatter.active_types = ['text/plain']
902 909
903 910 def test_user_expression():
904 911 # enable all formatters
905 912 ip.display_formatter.active_types = ip.display_formatter.format_types
906 913 query = {
907 914 'a' : '1 + 2',
908 915 'b' : '1/0',
909 916 }
910 917 r = ip.user_expressions(query)
911 918 import pprint
912 919 pprint.pprint(r)
913 920 nt.assert_equal(set(r.keys()), set(query.keys()))
914 921 a = r['a']
915 922 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
916 923 nt.assert_equal(a['status'], 'ok')
917 924 data = a['data']
918 925 metadata = a['metadata']
919 926 nt.assert_equal(data.get('text/plain'), '3')
920 927
921 928 b = r['b']
922 929 nt.assert_equal(b['status'], 'error')
923 930 nt.assert_equal(b['ename'], 'ZeroDivisionError')
924 931
925 932 # back to text only
926 933 ip.display_formatter.active_types = ['text/plain']
927 934
928 935
929 936 class TestSyntaxErrorTransformer(unittest.TestCase):
930 937 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
931 938
932 939 @staticmethod
933 940 def transformer(lines):
934 941 for line in lines:
935 942 pos = line.find('syntaxerror')
936 943 if pos >= 0:
937 944 e = SyntaxError('input contains "syntaxerror"')
938 945 e.text = line
939 946 e.offset = pos + 1
940 947 raise e
941 948 return lines
942 949
943 950 def setUp(self):
944 951 ip.input_transformers_post.append(self.transformer)
945 952
946 953 def tearDown(self):
947 954 ip.input_transformers_post.remove(self.transformer)
948 955
949 956 def test_syntaxerror_input_transformer(self):
950 957 with tt.AssertPrints('1234'):
951 958 ip.run_cell('1234')
952 959 with tt.AssertPrints('SyntaxError: invalid syntax'):
953 960 ip.run_cell('1 2 3') # plain python syntax error
954 961 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
955 962 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
956 963 with tt.AssertPrints('3456'):
957 964 ip.run_cell('3456')
958 965
959 966
960 967 class TestWarningSuppression(unittest.TestCase):
961 968 def test_warning_suppression(self):
962 969 ip.run_cell("import warnings")
963 970 try:
964 971 with self.assertWarnsRegex(UserWarning, "asdf"):
965 972 ip.run_cell("warnings.warn('asdf')")
966 973 # Here's the real test -- if we run that again, we should get the
967 974 # warning again. Traditionally, each warning was only issued once per
968 975 # IPython session (approximately), even if the user typed in new and
969 976 # different code that should have also triggered the warning, leading
970 977 # to much confusion.
971 978 with self.assertWarnsRegex(UserWarning, "asdf"):
972 979 ip.run_cell("warnings.warn('asdf')")
973 980 finally:
974 981 ip.run_cell("del warnings")
975 982
976 983
977 984 def test_deprecation_warning(self):
978 985 ip.run_cell("""
979 986 import warnings
980 987 def wrn():
981 988 warnings.warn(
982 989 "I AM A WARNING",
983 990 DeprecationWarning
984 991 )
985 992 """)
986 993 try:
987 994 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
988 995 ip.run_cell("wrn()")
989 996 finally:
990 997 ip.run_cell("del warnings")
991 998 ip.run_cell("del wrn")
992 999
993 1000
994 1001 class TestImportNoDeprecate(tt.TempFileMixin):
995 1002
996 1003 def setUp(self):
997 1004 """Make a valid python temp file."""
998 1005 self.mktmp("""
999 1006 import warnings
1000 1007 def wrn():
1001 1008 warnings.warn(
1002 1009 "I AM A WARNING",
1003 1010 DeprecationWarning
1004 1011 )
1005 1012 """)
1006 1013 super().setUp()
1007 1014
1008 1015 def test_no_dep(self):
1009 1016 """
1010 1017 No deprecation warning should be raised from imported functions
1011 1018 """
1012 1019 ip.run_cell("from {} import wrn".format(self.fname))
1013 1020
1014 1021 with tt.AssertNotPrints("I AM A WARNING"):
1015 1022 ip.run_cell("wrn()")
1016 1023 ip.run_cell("del wrn")
1017 1024
1018 1025
1019 1026 def test_custom_exc_count():
1020 1027 hook = mock.Mock(return_value=None)
1021 1028 ip.set_custom_exc((SyntaxError,), hook)
1022 1029 before = ip.execution_count
1023 1030 ip.run_cell("def foo()", store_history=True)
1024 1031 # restore default excepthook
1025 1032 ip.set_custom_exc((), None)
1026 1033 nt.assert_equal(hook.call_count, 1)
1027 1034 nt.assert_equal(ip.execution_count, before + 1)
1028 1035
1029 1036
1030 1037 def test_run_cell_async():
1031 1038 loop = asyncio.get_event_loop()
1032 1039 ip.run_cell("import asyncio")
1033 1040 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1034 1041 assert asyncio.iscoroutine(coro)
1035 1042 result = loop.run_until_complete(coro)
1036 1043 assert isinstance(result, interactiveshell.ExecutionResult)
1037 1044 assert result.result == 5
1038 1045
1039 1046
1040 1047 def test_should_run_async():
1041 1048 assert not ip.should_run_async("a = 5")
1042 1049 assert ip.should_run_async("await x")
1043 1050 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1044 1051
1045 1052
1046 1053 def test_set_custom_completer():
1047 1054 num_completers = len(ip.Completer.matchers)
1048 1055
1049 1056 def foo(*args, **kwargs):
1050 1057 return "I'm a completer!"
1051 1058
1052 1059 ip.set_custom_completer(foo, 0)
1053 1060
1054 1061 # check that we've really added a new completer
1055 1062 assert len(ip.Completer.matchers) == num_completers + 1
1056 1063
1057 1064 # check that the first completer is the function we defined
1058 1065 assert ip.Completer.matchers[0]() == "I'm a completer!"
1059 1066
1060 1067 # clean up
1061 1068 ip.Completer.custom_matchers.pop()
General Comments 0
You need to be logged in to leave comments. Login now