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