##// END OF EJS Templates
Merge pull request #11799 from Carreau/pytest-last-streak...
Matthias Bussonnier -
r25116:32cf29fb merge
parent child Browse files
Show More
@@ -1,1004 +1,1000 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 ip = get_ipython()
40 39
41 40 #-----------------------------------------------------------------------------
42 41 # Tests
43 42 #-----------------------------------------------------------------------------
44 43
45 44 class DerivedInterrupt(KeyboardInterrupt):
46 45 pass
47 46
48 47 class InteractiveShellTestCase(unittest.TestCase):
49 48 def test_naked_string_cells(self):
50 49 """Test that cells with only naked strings are fully executed"""
51 50 # First, single-line inputs
52 51 ip.run_cell('"a"\n')
53 52 self.assertEqual(ip.user_ns['_'], 'a')
54 53 # And also multi-line cells
55 54 ip.run_cell('"""a\nb"""\n')
56 55 self.assertEqual(ip.user_ns['_'], 'a\nb')
57 56
58 57 def test_run_empty_cell(self):
59 58 """Just make sure we don't get a horrible error with a blank
60 59 cell of input. Yes, I did overlook that."""
61 60 old_xc = ip.execution_count
62 61 res = ip.run_cell('')
63 62 self.assertEqual(ip.execution_count, old_xc)
64 63 self.assertEqual(res.execution_count, None)
65 64
66 65 def test_run_cell_multiline(self):
67 66 """Multi-block, multi-line cells must execute correctly.
68 67 """
69 68 src = '\n'.join(["x=1",
70 69 "y=2",
71 70 "if 1:",
72 71 " x += 1",
73 72 " y += 1",])
74 73 res = ip.run_cell(src)
75 74 self.assertEqual(ip.user_ns['x'], 2)
76 75 self.assertEqual(ip.user_ns['y'], 3)
77 76 self.assertEqual(res.success, True)
78 77 self.assertEqual(res.result, None)
79 78
80 79 def test_multiline_string_cells(self):
81 80 "Code sprinkled with multiline strings should execute (GH-306)"
82 81 ip.run_cell('tmp=0')
83 82 self.assertEqual(ip.user_ns['tmp'], 0)
84 83 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 84 self.assertEqual(ip.user_ns['tmp'], 1)
86 85 self.assertEqual(res.success, True)
87 86 self.assertEqual(res.result, "a\nb")
88 87
89 88 def test_dont_cache_with_semicolon(self):
90 89 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 90 oldlen = len(ip.user_ns['Out'])
92 91 for cell in ['1;', '1;1;']:
93 92 res = ip.run_cell(cell, store_history=True)
94 93 newlen = len(ip.user_ns['Out'])
95 94 self.assertEqual(oldlen, newlen)
96 95 self.assertIsNone(res.result)
97 96 i = 0
98 97 #also test the default caching behavior
99 98 for cell in ['1', '1;1']:
100 99 ip.run_cell(cell, store_history=True)
101 100 newlen = len(ip.user_ns['Out'])
102 101 i += 1
103 102 self.assertEqual(oldlen+i, newlen)
104 103
105 104 def test_syntax_error(self):
106 105 res = ip.run_cell("raise = 3")
107 106 self.assertIsInstance(res.error_before_exec, SyntaxError)
108 107
109 108 def test_In_variable(self):
110 109 "Verify that In variable grows with user input (GH-284)"
111 110 oldlen = len(ip.user_ns['In'])
112 111 ip.run_cell('1;', store_history=True)
113 112 newlen = len(ip.user_ns['In'])
114 113 self.assertEqual(oldlen+1, newlen)
115 114 self.assertEqual(ip.user_ns['In'][-1],'1;')
116 115
117 116 def test_magic_names_in_string(self):
118 117 ip.run_cell('a = """\n%exit\n"""')
119 118 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120 119
121 120 def test_trailing_newline(self):
122 121 """test that running !(command) does not raise a SyntaxError"""
123 122 ip.run_cell('!(true)\n', False)
124 123 ip.run_cell('!(true)\n\n\n', False)
125 124
126 125 def test_gh_597(self):
127 126 """Pretty-printing lists of objects with non-ascii reprs may cause
128 127 problems."""
129 128 class Spam(object):
130 def __repr__(self):
131 return "\xe9"*50
129 def __repr__(self):
130 return "\xe9"*50
132 131 import IPython.core.formatters
133 132 f = IPython.core.formatters.PlainTextFormatter()
134 133 f([Spam(),Spam()])
135 134
136 135
137 136 def test_future_flags(self):
138 137 """Check that future flags are used for parsing code (gh-777)"""
139 138 ip.run_cell('from __future__ import barry_as_FLUFL')
140 139 try:
141 140 ip.run_cell('prfunc_return_val = 1 <> 2')
142 141 assert 'prfunc_return_val' in ip.user_ns
143 142 finally:
144 143 # Reset compiler flags so we don't mess up other tests.
145 144 ip.compile.reset_compiler_flags()
146 145
147 146 def test_can_pickle(self):
148 147 "Can we pickle objects defined interactively (GH-29)"
149 148 ip = get_ipython()
150 149 ip.reset()
151 150 ip.run_cell(("class Mylist(list):\n"
152 151 " def __init__(self,x=[]):\n"
153 152 " list.__init__(self,x)"))
154 153 ip.run_cell("w=Mylist([1,2,3])")
155 154
156 155 from pickle import dumps
157 156
158 157 # We need to swap in our main module - this is only necessary
159 158 # inside the test framework, because IPython puts the interactive module
160 159 # in place (but the test framework undoes this).
161 160 _main = sys.modules['__main__']
162 161 sys.modules['__main__'] = ip.user_module
163 162 try:
164 163 res = dumps(ip.user_ns["w"])
165 164 finally:
166 165 sys.modules['__main__'] = _main
167 166 self.assertTrue(isinstance(res, bytes))
168 167
169 168 def test_global_ns(self):
170 169 "Code in functions must be able to access variables outside them."
171 170 ip = get_ipython()
172 171 ip.run_cell("a = 10")
173 172 ip.run_cell(("def f(x):\n"
174 173 " return x + a"))
175 174 ip.run_cell("b = f(12)")
176 175 self.assertEqual(ip.user_ns["b"], 22)
177 176
178 177 def test_bad_custom_tb(self):
179 178 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 179 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 180 self.assertEqual(ip.custom_exceptions, (IOError,))
182 181 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 182 ip.run_cell(u'raise IOError("foo")')
184 183 self.assertEqual(ip.custom_exceptions, ())
185 184
186 185 def test_bad_custom_tb_return(self):
187 186 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 187 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 188 self.assertEqual(ip.custom_exceptions, (NameError,))
190 189 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 190 ip.run_cell(u'a=abracadabra')
192 191 self.assertEqual(ip.custom_exceptions, ())
193 192
194 193 def test_drop_by_id(self):
195 194 myvars = {"a":object(), "b":object(), "c": object()}
196 195 ip.push(myvars, interactive=False)
197 196 for name in myvars:
198 197 assert name in ip.user_ns, name
199 198 assert name in ip.user_ns_hidden, name
200 199 ip.user_ns['b'] = 12
201 200 ip.drop_by_id(myvars)
202 201 for name in ["a", "c"]:
203 202 assert name not in ip.user_ns, name
204 203 assert name not in ip.user_ns_hidden, name
205 204 assert ip.user_ns['b'] == 12
206 205 ip.reset()
207 206
208 207 def test_var_expand(self):
209 208 ip.user_ns['f'] = u'Ca\xf1o'
210 209 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
211 210 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 211 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 212 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214 213
215 214 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216 215
217 216 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 217 # This should not raise any exception:
219 218 ip.var_expand(u'echo $f')
220 219
221 220 def test_var_expand_local(self):
222 221 """Test local variable expansion in !system and %magic calls"""
223 222 # !system
224 223 ip.run_cell('def test():\n'
225 224 ' lvar = "ttt"\n'
226 225 ' ret = !echo {lvar}\n'
227 226 ' return ret[0]\n')
228 227 res = ip.user_ns['test']()
229 228 nt.assert_in('ttt', res)
230 229
231 230 # %magic
232 231 ip.run_cell('def makemacro():\n'
233 232 ' macroname = "macro_var_expand_locals"\n'
234 233 ' %macro {macroname} codestr\n')
235 234 ip.user_ns['codestr'] = "str(12)"
236 235 ip.run_cell('makemacro()')
237 236 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238 237
239 238 def test_var_expand_self(self):
240 239 """Test variable expansion with the name 'self', which was failing.
241 240
242 241 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 242 """
244 243 ip.run_cell('class cTest:\n'
245 244 ' classvar="see me"\n'
246 245 ' def test(self):\n'
247 246 ' res = !echo Variable: {self.classvar}\n'
248 247 ' return res[0]\n')
249 248 nt.assert_in('see me', ip.user_ns['cTest']().test())
250 249
251 250 def test_bad_var_expand(self):
252 251 """var_expand on invalid formats shouldn't raise"""
253 252 # SyntaxError
254 253 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 254 # NameError
256 255 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 256 # ZeroDivisionError
258 257 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259 258
260 259 def test_silent_postexec(self):
261 260 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 261 pre_explicit = mock.Mock()
263 262 pre_always = mock.Mock()
264 263 post_explicit = mock.Mock()
265 264 post_always = mock.Mock()
266 265 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
267 266
268 267 ip.events.register('pre_run_cell', pre_explicit)
269 268 ip.events.register('pre_execute', pre_always)
270 269 ip.events.register('post_run_cell', post_explicit)
271 270 ip.events.register('post_execute', post_always)
272 271
273 272 try:
274 273 ip.run_cell("1", silent=True)
275 274 assert pre_always.called
276 275 assert not pre_explicit.called
277 276 assert post_always.called
278 277 assert not post_explicit.called
279 278 # double-check that non-silent exec did what we expected
280 279 # silent to avoid
281 280 ip.run_cell("1")
282 281 assert pre_explicit.called
283 282 assert post_explicit.called
284 283 info, = pre_explicit.call_args[0]
285 284 result, = post_explicit.call_args[0]
286 285 self.assertEqual(info, result.info)
287 286 # check that post hooks are always called
288 287 [m.reset_mock() for m in all_mocks]
289 288 ip.run_cell("syntax error")
290 289 assert pre_always.called
291 290 assert pre_explicit.called
292 291 assert post_always.called
293 292 assert post_explicit.called
294 293 info, = pre_explicit.call_args[0]
295 294 result, = post_explicit.call_args[0]
296 295 self.assertEqual(info, result.info)
297 296 finally:
298 297 # remove post-exec
299 298 ip.events.unregister('pre_run_cell', pre_explicit)
300 299 ip.events.unregister('pre_execute', pre_always)
301 300 ip.events.unregister('post_run_cell', post_explicit)
302 301 ip.events.unregister('post_execute', post_always)
303 302
304 303 def test_silent_noadvance(self):
305 304 """run_cell(silent=True) doesn't advance execution_count"""
306 305 ec = ip.execution_count
307 306 # silent should force store_history=False
308 307 ip.run_cell("1", store_history=True, silent=True)
309 308
310 309 self.assertEqual(ec, ip.execution_count)
311 310 # double-check that non-silent exec did what we expected
312 311 # silent to avoid
313 312 ip.run_cell("1", store_history=True)
314 313 self.assertEqual(ec+1, ip.execution_count)
315 314
316 315 def test_silent_nodisplayhook(self):
317 316 """run_cell(silent=True) doesn't trigger displayhook"""
318 317 d = dict(called=False)
319 318
320 319 trap = ip.display_trap
321 320 save_hook = trap.hook
322 321
323 322 def failing_hook(*args, **kwargs):
324 323 d['called'] = True
325 324
326 325 try:
327 326 trap.hook = failing_hook
328 327 res = ip.run_cell("1", silent=True)
329 328 self.assertFalse(d['called'])
330 329 self.assertIsNone(res.result)
331 330 # double-check that non-silent exec did what we expected
332 331 # silent to avoid
333 332 ip.run_cell("1")
334 333 self.assertTrue(d['called'])
335 334 finally:
336 335 trap.hook = save_hook
337 336
338 337 def test_ofind_line_magic(self):
339 338 from IPython.core.magic import register_line_magic
340 339
341 340 @register_line_magic
342 341 def lmagic(line):
343 342 "A line magic"
344 343
345 344 # Get info on line magic
346 345 lfind = ip._ofind('lmagic')
347 346 info = dict(found=True, isalias=False, ismagic=True,
348 347 namespace = 'IPython internal', obj= lmagic.__wrapped__,
349 348 parent = None)
350 349 nt.assert_equal(lfind, info)
351 350
352 351 def test_ofind_cell_magic(self):
353 352 from IPython.core.magic import register_cell_magic
354 353
355 354 @register_cell_magic
356 355 def cmagic(line, cell):
357 356 "A cell magic"
358 357
359 358 # Get info on cell magic
360 359 find = ip._ofind('cmagic')
361 360 info = dict(found=True, isalias=False, ismagic=True,
362 361 namespace = 'IPython internal', obj= cmagic.__wrapped__,
363 362 parent = None)
364 363 nt.assert_equal(find, info)
365 364
366 365 def test_ofind_property_with_error(self):
367 366 class A(object):
368 367 @property
369 368 def foo(self):
370 369 raise NotImplementedError()
371 370 a = A()
372 371
373 372 found = ip._ofind('a.foo', [('locals', locals())])
374 373 info = dict(found=True, isalias=False, ismagic=False,
375 374 namespace='locals', obj=A.foo, parent=a)
376 375 nt.assert_equal(found, info)
377 376
378 377 def test_ofind_multiple_attribute_lookups(self):
379 378 class A(object):
380 379 @property
381 380 def foo(self):
382 381 raise NotImplementedError()
383 382
384 383 a = A()
385 384 a.a = A()
386 385 a.a.a = A()
387 386
388 387 found = ip._ofind('a.a.a.foo', [('locals', locals())])
389 388 info = dict(found=True, isalias=False, ismagic=False,
390 389 namespace='locals', obj=A.foo, parent=a.a.a)
391 390 nt.assert_equal(found, info)
392 391
393 392 def test_ofind_slotted_attributes(self):
394 393 class A(object):
395 394 __slots__ = ['foo']
396 395 def __init__(self):
397 396 self.foo = 'bar'
398 397
399 398 a = A()
400 399 found = ip._ofind('a.foo', [('locals', locals())])
401 400 info = dict(found=True, isalias=False, ismagic=False,
402 401 namespace='locals', obj=a.foo, parent=a)
403 402 nt.assert_equal(found, info)
404 403
405 404 found = ip._ofind('a.bar', [('locals', locals())])
406 405 info = dict(found=False, isalias=False, ismagic=False,
407 406 namespace=None, obj=None, parent=a)
408 407 nt.assert_equal(found, info)
409 408
410 409 def test_ofind_prefers_property_to_instance_level_attribute(self):
411 410 class A(object):
412 411 @property
413 412 def foo(self):
414 413 return 'bar'
415 414 a = A()
416 415 a.__dict__['foo'] = 'baz'
417 416 nt.assert_equal(a.foo, 'bar')
418 417 found = ip._ofind('a.foo', [('locals', locals())])
419 418 nt.assert_is(found['obj'], A.foo)
420 419
421 420 def test_custom_syntaxerror_exception(self):
422 421 called = []
423 422 def my_handler(shell, etype, value, tb, tb_offset=None):
424 423 called.append(etype)
425 424 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
426 425
427 426 ip.set_custom_exc((SyntaxError,), my_handler)
428 427 try:
429 428 ip.run_cell("1f")
430 429 # Check that this was called, and only once.
431 430 self.assertEqual(called, [SyntaxError])
432 431 finally:
433 432 # Reset the custom exception hook
434 433 ip.set_custom_exc((), None)
435 434
436 435 def test_custom_exception(self):
437 436 called = []
438 437 def my_handler(shell, etype, value, tb, tb_offset=None):
439 438 called.append(etype)
440 439 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
441 440
442 441 ip.set_custom_exc((ValueError,), my_handler)
443 442 try:
444 443 res = ip.run_cell("raise ValueError('test')")
445 444 # Check that this was called, and only once.
446 445 self.assertEqual(called, [ValueError])
447 446 # Check that the error is on the result object
448 447 self.assertIsInstance(res.error_in_exec, ValueError)
449 448 finally:
450 449 # Reset the custom exception hook
451 450 ip.set_custom_exc((), None)
452 451
453 452 def test_mktempfile(self):
454 453 filename = ip.mktempfile()
455 454 # Check that we can open the file again on Windows
456 455 with open(filename, 'w') as f:
457 456 f.write('abc')
458 457
459 458 filename = ip.mktempfile(data='blah')
460 459 with open(filename, 'r') as f:
461 460 self.assertEqual(f.read(), 'blah')
462 461
463 462 def test_new_main_mod(self):
464 463 # Smoketest to check that this accepts a unicode module name
465 464 name = u'jiefmw'
466 465 mod = ip.new_main_mod(u'%s.py' % name, name)
467 466 self.assertEqual(mod.__name__, name)
468 467
469 468 def test_get_exception_only(self):
470 469 try:
471 470 raise KeyboardInterrupt
472 471 except KeyboardInterrupt:
473 472 msg = ip.get_exception_only()
474 473 self.assertEqual(msg, 'KeyboardInterrupt\n')
475 474
476 475 try:
477 476 raise DerivedInterrupt("foo")
478 477 except KeyboardInterrupt:
479 478 msg = ip.get_exception_only()
480 479 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
481 480
482 481 def test_inspect_text(self):
483 482 ip.run_cell('a = 5')
484 483 text = ip.object_inspect_text('a')
485 484 self.assertIsInstance(text, str)
486 485
487 486 def test_last_execution_result(self):
488 487 """ Check that last execution result gets set correctly (GH-10702) """
489 488 result = ip.run_cell('a = 5; a')
490 489 self.assertTrue(ip.last_execution_succeeded)
491 490 self.assertEqual(ip.last_execution_result.result, 5)
492 491
493 492 result = ip.run_cell('a = x_invalid_id_x')
494 493 self.assertFalse(ip.last_execution_succeeded)
495 494 self.assertFalse(ip.last_execution_result.success)
496 495 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
497 496
498 497 def test_reset_aliasing(self):
499 498 """ Check that standard posix aliases work after %reset. """
500 499 if os.name != 'posix':
501 500 return
502 501
503 502 ip.reset()
504 503 for cmd in ('clear', 'more', 'less', 'man'):
505 504 res = ip.run_cell('%' + cmd)
506 505 self.assertEqual(res.success, True)
507 506
508 507
509 508 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
510 509
511 510 @onlyif_unicode_paths
512 511 def setUp(self):
513 512 self.BASETESTDIR = tempfile.mkdtemp()
514 513 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
515 514 os.mkdir(self.TESTDIR)
516 515 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
517 516 sfile.write("pass\n")
518 517 self.oldpath = os.getcwd()
519 518 os.chdir(self.TESTDIR)
520 519 self.fname = u"Γ₯Àâtestscript.py"
521 520
522 521 def tearDown(self):
523 522 os.chdir(self.oldpath)
524 523 shutil.rmtree(self.BASETESTDIR)
525 524
526 525 @onlyif_unicode_paths
527 526 def test_1(self):
528 527 """Test safe_execfile with non-ascii path
529 528 """
530 529 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
531 530
532 531 class ExitCodeChecks(tt.TempFileMixin):
533 532
534 533 def setUp(self):
535 534 self.system = ip.system_raw
536 535
537 536 def test_exit_code_ok(self):
538 537 self.system('exit 0')
539 538 self.assertEqual(ip.user_ns['_exit_code'], 0)
540 539
541 540 def test_exit_code_error(self):
542 541 self.system('exit 1')
543 542 self.assertEqual(ip.user_ns['_exit_code'], 1)
544 543
545 544 @skipif(not hasattr(signal, 'SIGALRM'))
546 545 def test_exit_code_signal(self):
547 546 self.mktmp("import signal, time\n"
548 547 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
549 548 "time.sleep(1)\n")
550 549 self.system("%s %s" % (sys.executable, self.fname))
551 550 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
552 551
553 552 @onlyif_cmds_exist("csh")
554 553 def test_exit_code_signal_csh(self):
555 554 SHELL = os.environ.get('SHELL', None)
556 555 os.environ['SHELL'] = find_cmd("csh")
557 556 try:
558 557 self.test_exit_code_signal()
559 558 finally:
560 559 if SHELL is not None:
561 560 os.environ['SHELL'] = SHELL
562 561 else:
563 562 del os.environ['SHELL']
564 563
565 564
566 565 class TestSystemRaw(ExitCodeChecks):
567 566
568 567 def setUp(self):
569 568 super().setUp()
570 569 self.sytem = ip.system_raw
571 570
572 571 @onlyif_unicode_paths
573 572 def test_1(self):
574 573 """Test system_raw with non-ascii cmd
575 574 """
576 575 cmd = u'''python -c "'Γ₯Àâ'" '''
577 576 ip.system_raw(cmd)
578 577
579 578 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
580 579 @mock.patch('os.system', side_effect=KeyboardInterrupt)
581 580 def test_control_c(self, *mocks):
582 581 try:
583 582 self.system("sleep 1 # wont happen")
584 583 except KeyboardInterrupt:
585 584 self.fail("system call should intercept "
586 585 "keyboard interrupt from subprocess.call")
587 586 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
588 587
589 588 # TODO: Exit codes are currently ignored on Windows.
590 589 class TestSystemPipedExitCode(ExitCodeChecks):
591 590
592 591 def setUp(self):
593 592 super().setUp()
594 593 self.sytem = ip.system_piped
595 594
596 595 @skip_win32
597 596 def test_exit_code_ok(self):
598 597 ExitCodeChecks.test_exit_code_ok(self)
599 598
600 599 @skip_win32
601 600 def test_exit_code_error(self):
602 601 ExitCodeChecks.test_exit_code_error(self)
603 602
604 603 @skip_win32
605 604 def test_exit_code_signal(self):
606 605 ExitCodeChecks.test_exit_code_signal(self)
607 606
608 607 class TestModules(tt.TempFileMixin):
609 608 def test_extraneous_loads(self):
610 609 """Test we're not loading modules on startup that we shouldn't.
611 610 """
612 611 self.mktmp("import sys\n"
613 612 "print('numpy' in sys.modules)\n"
614 613 "print('ipyparallel' in sys.modules)\n"
615 614 "print('ipykernel' in sys.modules)\n"
616 615 )
617 616 out = "False\nFalse\nFalse\n"
618 617 tt.ipexec_validate(self.fname, out)
619 618
620 619 class Negator(ast.NodeTransformer):
621 620 """Negates all number literals in an AST."""
622 621
623 622 # for python 3.7 and earlier
624 623 def visit_Num(self, node):
625 624 node.n = -node.n
626 625 return node
627 626
628 627 # for python 3.8+
629 628 def visit_Constant(self, node):
630 629 if isinstance(node.value, int):
631 630 return self.visit_Num(node)
632 631 return node
633 632
634 633 class TestAstTransform(unittest.TestCase):
635 634 def setUp(self):
636 635 self.negator = Negator()
637 636 ip.ast_transformers.append(self.negator)
638 637
639 638 def tearDown(self):
640 639 ip.ast_transformers.remove(self.negator)
641 640
642 641 def test_run_cell(self):
643 642 with tt.AssertPrints('-34'):
644 643 ip.run_cell('print (12 + 22)')
645 644
646 645 # A named reference to a number shouldn't be transformed.
647 646 ip.user_ns['n'] = 55
648 647 with tt.AssertNotPrints('-55'):
649 648 ip.run_cell('print (n)')
650 649
651 650 def test_timeit(self):
652 651 called = set()
653 652 def f(x):
654 653 called.add(x)
655 654 ip.push({'f':f})
656 655
657 656 with tt.AssertPrints("std. dev. of"):
658 657 ip.run_line_magic("timeit", "-n1 f(1)")
659 658 self.assertEqual(called, {-1})
660 659 called.clear()
661 660
662 661 with tt.AssertPrints("std. dev. of"):
663 662 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
664 663 self.assertEqual(called, {-2, -3})
665 664
666 665 def test_time(self):
667 666 called = []
668 667 def f(x):
669 668 called.append(x)
670 669 ip.push({'f':f})
671 670
672 671 # Test with an expression
673 672 with tt.AssertPrints("Wall time: "):
674 673 ip.run_line_magic("time", "f(5+9)")
675 674 self.assertEqual(called, [-14])
676 675 called[:] = []
677 676
678 677 # Test with a statement (different code path)
679 678 with tt.AssertPrints("Wall time: "):
680 679 ip.run_line_magic("time", "a = f(-3 + -2)")
681 680 self.assertEqual(called, [5])
682 681
683 682 def test_macro(self):
684 683 ip.push({'a':10})
685 684 # The AST transformation makes this do a+=-1
686 685 ip.define_macro("amacro", "a+=1\nprint(a)")
687 686
688 687 with tt.AssertPrints("9"):
689 688 ip.run_cell("amacro")
690 689 with tt.AssertPrints("8"):
691 690 ip.run_cell("amacro")
692 691
693 692 class IntegerWrapper(ast.NodeTransformer):
694 693 """Wraps all integers in a call to Integer()"""
695 694
696 695 # for Python 3.7 and earlier
697 696
698 697 # for Python 3.7 and earlier
699 698 def visit_Num(self, node):
700 699 if isinstance(node.n, int):
701 700 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
702 701 args=[node], keywords=[])
703 702 return node
704 703
705 704 # For Python 3.8+
706 705 def visit_Constant(self, node):
707 706 if isinstance(node.value, int):
708 707 return self.visit_Num(node)
709 708 return node
710 709
711 710
712 711 class TestAstTransform2(unittest.TestCase):
713 712 def setUp(self):
714 713 self.intwrapper = IntegerWrapper()
715 714 ip.ast_transformers.append(self.intwrapper)
716 715
717 716 self.calls = []
718 717 def Integer(*args):
719 718 self.calls.append(args)
720 719 return args
721 720 ip.push({"Integer": Integer})
722 721
723 722 def tearDown(self):
724 723 ip.ast_transformers.remove(self.intwrapper)
725 724 del ip.user_ns['Integer']
726 725
727 726 def test_run_cell(self):
728 727 ip.run_cell("n = 2")
729 728 self.assertEqual(self.calls, [(2,)])
730 729
731 730 # This shouldn't throw an error
732 731 ip.run_cell("o = 2.0")
733 732 self.assertEqual(ip.user_ns['o'], 2.0)
734 733
735 734 def test_timeit(self):
736 735 called = set()
737 736 def f(x):
738 737 called.add(x)
739 738 ip.push({'f':f})
740 739
741 740 with tt.AssertPrints("std. dev. of"):
742 741 ip.run_line_magic("timeit", "-n1 f(1)")
743 742 self.assertEqual(called, {(1,)})
744 743 called.clear()
745 744
746 745 with tt.AssertPrints("std. dev. of"):
747 746 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
748 747 self.assertEqual(called, {(2,), (3,)})
749 748
750 749 class ErrorTransformer(ast.NodeTransformer):
751 750 """Throws an error when it sees a number."""
752 751
753 752 # for Python 3.7 and earlier
754 753 def visit_Num(self, node):
755 754 raise ValueError("test")
756 755
757 756 # for Python 3.8+
758 757 def visit_Constant(self, node):
759 758 if isinstance(node.value, int):
760 759 return self.visit_Num(node)
761 760 return node
762 761
763 762
764 763 class TestAstTransformError(unittest.TestCase):
765 764 def test_unregistering(self):
766 765 err_transformer = ErrorTransformer()
767 766 ip.ast_transformers.append(err_transformer)
768 767
769 with tt.AssertPrints("unregister", channel='stderr'):
768 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
770 769 ip.run_cell("1 + 2")
771 770
772 771 # This should have been removed.
773 772 nt.assert_not_in(err_transformer, ip.ast_transformers)
774 773
775 774
776 775 class StringRejector(ast.NodeTransformer):
777 776 """Throws an InputRejected when it sees a string literal.
778 777
779 778 Used to verify that NodeTransformers can signal that a piece of code should
780 779 not be executed by throwing an InputRejected.
781 780 """
782 781
783 782 #for python 3.7 and earlier
784 783 def visit_Str(self, node):
785 784 raise InputRejected("test")
786 785
787 786 # 3.8 only
788 787 def visit_Constant(self, node):
789 788 if isinstance(node.value, str):
790 789 raise InputRejected("test")
791 790 return node
792 791
793 792
794 793 class TestAstTransformInputRejection(unittest.TestCase):
795 794
796 795 def setUp(self):
797 796 self.transformer = StringRejector()
798 797 ip.ast_transformers.append(self.transformer)
799 798
800 799 def tearDown(self):
801 800 ip.ast_transformers.remove(self.transformer)
802 801
803 802 def test_input_rejection(self):
804 803 """Check that NodeTransformers can reject input."""
805 804
806 805 expect_exception_tb = tt.AssertPrints("InputRejected: test")
807 806 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
808 807
809 808 # Run the same check twice to verify that the transformer is not
810 809 # disabled after raising.
811 810 with expect_exception_tb, expect_no_cell_output:
812 811 ip.run_cell("'unsafe'")
813 812
814 813 with expect_exception_tb, expect_no_cell_output:
815 814 res = ip.run_cell("'unsafe'")
816 815
817 816 self.assertIsInstance(res.error_before_exec, InputRejected)
818 817
819 818 def test__IPYTHON__():
820 819 # This shouldn't raise a NameError, that's all
821 820 __IPYTHON__
822 821
823 822
824 823 class DummyRepr(object):
825 824 def __repr__(self):
826 825 return "DummyRepr"
827 826
828 827 def _repr_html_(self):
829 828 return "<b>dummy</b>"
830 829
831 830 def _repr_javascript_(self):
832 831 return "console.log('hi');", {'key': 'value'}
833 832
834 833
835 834 def test_user_variables():
836 835 # enable all formatters
837 836 ip.display_formatter.active_types = ip.display_formatter.format_types
838 837
839 838 ip.user_ns['dummy'] = d = DummyRepr()
840 839 keys = {'dummy', 'doesnotexist'}
841 840 r = ip.user_expressions({ key:key for key in keys})
842 841
843 842 nt.assert_equal(keys, set(r.keys()))
844 843 dummy = r['dummy']
845 844 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
846 845 nt.assert_equal(dummy['status'], 'ok')
847 846 data = dummy['data']
848 847 metadata = dummy['metadata']
849 848 nt.assert_equal(data.get('text/html'), d._repr_html_())
850 849 js, jsmd = d._repr_javascript_()
851 850 nt.assert_equal(data.get('application/javascript'), js)
852 851 nt.assert_equal(metadata.get('application/javascript'), jsmd)
853 852
854 853 dne = r['doesnotexist']
855 854 nt.assert_equal(dne['status'], 'error')
856 855 nt.assert_equal(dne['ename'], 'NameError')
857 856
858 857 # back to text only
859 858 ip.display_formatter.active_types = ['text/plain']
860 859
861 860 def test_user_expression():
862 861 # enable all formatters
863 862 ip.display_formatter.active_types = ip.display_formatter.format_types
864 863 query = {
865 864 'a' : '1 + 2',
866 865 'b' : '1/0',
867 866 }
868 867 r = ip.user_expressions(query)
869 868 import pprint
870 869 pprint.pprint(r)
871 870 nt.assert_equal(set(r.keys()), set(query.keys()))
872 871 a = r['a']
873 872 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
874 873 nt.assert_equal(a['status'], 'ok')
875 874 data = a['data']
876 875 metadata = a['metadata']
877 876 nt.assert_equal(data.get('text/plain'), '3')
878 877
879 878 b = r['b']
880 879 nt.assert_equal(b['status'], 'error')
881 880 nt.assert_equal(b['ename'], 'ZeroDivisionError')
882 881
883 882 # back to text only
884 883 ip.display_formatter.active_types = ['text/plain']
885
886
887
888 884
889 885
890 886 class TestSyntaxErrorTransformer(unittest.TestCase):
891 887 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
892 888
893 889 @staticmethod
894 890 def transformer(lines):
895 891 for line in lines:
896 892 pos = line.find('syntaxerror')
897 893 if pos >= 0:
898 894 e = SyntaxError('input contains "syntaxerror"')
899 895 e.text = line
900 896 e.offset = pos + 1
901 897 raise e
902 898 return lines
903 899
904 900 def setUp(self):
905 901 ip.input_transformers_post.append(self.transformer)
906 902
907 903 def tearDown(self):
908 904 ip.input_transformers_post.remove(self.transformer)
909 905
910 906 def test_syntaxerror_input_transformer(self):
911 907 with tt.AssertPrints('1234'):
912 908 ip.run_cell('1234')
913 909 with tt.AssertPrints('SyntaxError: invalid syntax'):
914 910 ip.run_cell('1 2 3') # plain python syntax error
915 911 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
916 912 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
917 913 with tt.AssertPrints('3456'):
918 914 ip.run_cell('3456')
919 915
920 916
921
922 def test_warning_suppression():
923 ip.run_cell("import warnings")
924 try:
925 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
926 ip.run_cell("warnings.warn('asdf')")
927 # Here's the real test -- if we run that again, we should get the
928 # warning again. Traditionally, each warning was only issued once per
929 # IPython session (approximately), even if the user typed in new and
930 # different code that should have also triggered the warning, leading
931 # to much confusion.
932 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
933 ip.run_cell("warnings.warn('asdf')")
934 finally:
935 ip.run_cell("del warnings")
917 class TestWarningSupression(unittest.TestCase):
918 def test_warning_suppression(self):
919 ip.run_cell("import warnings")
920 try:
921 with self.assertWarnsRegex(UserWarning, "asdf"):
922 ip.run_cell("warnings.warn('asdf')")
923 # Here's the real test -- if we run that again, we should get the
924 # warning again. Traditionally, each warning was only issued once per
925 # IPython session (approximately), even if the user typed in new and
926 # different code that should have also triggered the warning, leading
927 # to much confusion.
928 with self.assertWarnsRegex(UserWarning, "asdf"):
929 ip.run_cell("warnings.warn('asdf')")
930 finally:
931 ip.run_cell("del warnings")
936 932
937 933
938 def test_deprecation_warning():
939 ip.run_cell("""
934 def test_deprecation_warning(self):
935 ip.run_cell("""
940 936 import warnings
941 937 def wrn():
942 938 warnings.warn(
943 939 "I AM A WARNING",
944 940 DeprecationWarning
945 941 )
946 942 """)
947 try:
948 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
949 ip.run_cell("wrn()")
950 finally:
951 ip.run_cell("del warnings")
952 ip.run_cell("del wrn")
943 try:
944 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
945 ip.run_cell("wrn()")
946 finally:
947 ip.run_cell("del warnings")
948 ip.run_cell("del wrn")
953 949
954 950
955 951 class TestImportNoDeprecate(tt.TempFileMixin):
956 952
957 953 def setUp(self):
958 954 """Make a valid python temp file."""
959 955 self.mktmp("""
960 956 import warnings
961 957 def wrn():
962 958 warnings.warn(
963 959 "I AM A WARNING",
964 960 DeprecationWarning
965 961 )
966 962 """)
967 963 super().setUp()
968 964
969 965 def test_no_dep(self):
970 966 """
971 967 No deprecation warning should be raised from imported functions
972 968 """
973 969 ip.run_cell("from {} import wrn".format(self.fname))
974 970
975 971 with tt.AssertNotPrints("I AM A WARNING"):
976 972 ip.run_cell("wrn()")
977 973 ip.run_cell("del wrn")
978 974
979 975
980 976 def test_custom_exc_count():
981 977 hook = mock.Mock(return_value=None)
982 978 ip.set_custom_exc((SyntaxError,), hook)
983 979 before = ip.execution_count
984 980 ip.run_cell("def foo()", store_history=True)
985 981 # restore default excepthook
986 982 ip.set_custom_exc((), None)
987 983 nt.assert_equal(hook.call_count, 1)
988 984 nt.assert_equal(ip.execution_count, before + 1)
989 985
990 986
991 987 def test_run_cell_async():
992 988 loop = asyncio.get_event_loop()
993 989 ip.run_cell("import asyncio")
994 990 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
995 991 assert asyncio.iscoroutine(coro)
996 992 result = loop.run_until_complete(coro)
997 993 assert isinstance(result, interactiveshell.ExecutionResult)
998 994 assert result.result == 5
999 995
1000 996
1001 997 def test_should_run_async():
1002 998 assert not ip.should_run_async("a = 5")
1003 999 assert ip.should_run_async("await x")
1004 1000 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
@@ -1,197 +1,196 b''
1 1 """Tests for various magic functions specific to the terminal frontend.
2 2
3 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Imports
8 8 #-----------------------------------------------------------------------------
9 9
10 10 import sys
11 11 from io import StringIO
12 12 from unittest import TestCase
13 13
14 14 import nose.tools as nt
15 15
16 16 from IPython.testing import tools as tt
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Globals
20 20 #-----------------------------------------------------------------------------
21 ip = get_ipython()
22 21
23 22 #-----------------------------------------------------------------------------
24 23 # Test functions begin
25 24 #-----------------------------------------------------------------------------
26 25
27 26 def check_cpaste(code, should_fail=False):
28 27 """Execute code via 'cpaste' and ensure it was executed, unless
29 28 should_fail is set.
30 29 """
31 30 ip.user_ns['code_ran'] = False
32 31
33 32 src = StringIO()
34 33 if not hasattr(src, 'encoding'):
35 34 # IPython expects stdin to have an encoding attribute
36 35 src.encoding = None
37 36 src.write(code)
38 37 src.write('\n--\n')
39 38 src.seek(0)
40 39
41 40 stdin_save = sys.stdin
42 41 sys.stdin = src
43 42
44 43 try:
45 44 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
46 45 with context("Traceback (most recent call last)"):
47 46 ip.magic('cpaste')
48 47
49 48 if not should_fail:
50 49 assert ip.user_ns['code_ran'], "%r failed" % code
51 50 finally:
52 51 sys.stdin = stdin_save
53 52
54 53 def test_cpaste():
55 54 """Test cpaste magic"""
56 55
57 56 def runf():
58 57 """Marker function: sets a flag when executed.
59 58 """
60 59 ip.user_ns['code_ran'] = True
61 60 return 'runf' # return string so '+ runf()' doesn't result in success
62 61
63 62 tests = {'pass': ["runf()",
64 63 "In [1]: runf()",
65 64 "In [1]: if 1:\n ...: runf()",
66 65 "> > > runf()",
67 66 ">>> runf()",
68 67 " >>> runf()",
69 68 ],
70 69
71 70 'fail': ["1 + runf()",
72 71 "++ runf()",
73 72 ]}
74 73
75 74 ip.user_ns['runf'] = runf
76 75
77 76 for code in tests['pass']:
78 77 check_cpaste(code)
79 78
80 79 for code in tests['fail']:
81 80 check_cpaste(code, should_fail=True)
82 81
83 82
84 83 class PasteTestCase(TestCase):
85 84 """Multiple tests for clipboard pasting"""
86 85
87 86 def paste(self, txt, flags='-q'):
88 87 """Paste input text, by default in quiet mode"""
89 88 ip.hooks.clipboard_get = lambda : txt
90 89 ip.magic('paste '+flags)
91 90
92 91 def setUp(self):
93 92 # Inject fake clipboard hook but save original so we can restore it later
94 93 self.original_clip = ip.hooks.clipboard_get
95 94
96 95 def tearDown(self):
97 96 # Restore original hook
98 97 ip.hooks.clipboard_get = self.original_clip
99 98
100 99 def test_paste(self):
101 100 ip.user_ns.pop('x', None)
102 101 self.paste('x = 1')
103 102 nt.assert_equal(ip.user_ns['x'], 1)
104 103 ip.user_ns.pop('x')
105 104
106 105 def test_paste_pyprompt(self):
107 106 ip.user_ns.pop('x', None)
108 107 self.paste('>>> x=2')
109 108 nt.assert_equal(ip.user_ns['x'], 2)
110 109 ip.user_ns.pop('x')
111 110
112 111 def test_paste_py_multi(self):
113 112 self.paste("""
114 113 >>> x = [1,2,3]
115 114 >>> y = []
116 115 >>> for i in x:
117 116 ... y.append(i**2)
118 117 ...
119 118 """)
120 119 nt.assert_equal(ip.user_ns['x'], [1,2,3])
121 120 nt.assert_equal(ip.user_ns['y'], [1,4,9])
122 121
123 122 def test_paste_py_multi_r(self):
124 123 "Now, test that self.paste -r works"
125 124 self.test_paste_py_multi()
126 125 nt.assert_equal(ip.user_ns.pop('x'), [1,2,3])
127 126 nt.assert_equal(ip.user_ns.pop('y'), [1,4,9])
128 127 nt.assert_false('x' in ip.user_ns)
129 128 ip.magic('paste -r')
130 129 nt.assert_equal(ip.user_ns['x'], [1,2,3])
131 130 nt.assert_equal(ip.user_ns['y'], [1,4,9])
132 131
133 132 def test_paste_email(self):
134 133 "Test pasting of email-quoted contents"
135 134 self.paste("""\
136 135 >> def foo(x):
137 136 >> return x + 1
138 137 >> xx = foo(1.1)""")
139 138 nt.assert_equal(ip.user_ns['xx'], 2.1)
140 139
141 140 def test_paste_email2(self):
142 141 "Email again; some programs add a space also at each quoting level"
143 142 self.paste("""\
144 143 > > def foo(x):
145 144 > > return x + 1
146 145 > > yy = foo(2.1) """)
147 146 nt.assert_equal(ip.user_ns['yy'], 3.1)
148 147
149 148 def test_paste_email_py(self):
150 149 "Email quoting of interactive input"
151 150 self.paste("""\
152 151 >> >>> def f(x):
153 152 >> ... return x+1
154 153 >> ...
155 154 >> >>> zz = f(2.5) """)
156 155 nt.assert_equal(ip.user_ns['zz'], 3.5)
157 156
158 157 def test_paste_echo(self):
159 158 "Also test self.paste echoing, by temporarily faking the writer"
160 159 w = StringIO()
161 160 writer = ip.write
162 161 ip.write = w.write
163 162 code = """
164 163 a = 100
165 164 b = 200"""
166 165 try:
167 166 self.paste(code,'')
168 167 out = w.getvalue()
169 168 finally:
170 169 ip.write = writer
171 170 nt.assert_equal(ip.user_ns['a'], 100)
172 171 nt.assert_equal(ip.user_ns['b'], 200)
173 nt.assert_equal(out, code+"\n## -- End pasted text --\n")
172 assert out == code+"\n## -- End pasted text --\n"
174 173
175 174 def test_paste_leading_commas(self):
176 175 "Test multiline strings with leading commas"
177 176 tm = ip.magics_manager.registry['TerminalMagics']
178 177 s = '''\
179 178 a = """
180 179 ,1,2,3
181 180 """'''
182 181 ip.user_ns.pop('foo', None)
183 182 tm.store_or_execute(s, 'foo')
184 183 nt.assert_in('foo', ip.user_ns)
185 184
186 185
187 186 def test_paste_trailing_question(self):
188 187 "Test pasting sources with trailing question marks"
189 188 tm = ip.magics_manager.registry['TerminalMagics']
190 189 s = '''\
191 190 def funcfoo():
192 191 if True: #am i true?
193 192 return 'fooresult'
194 193 '''
195 194 ip.user_ns.pop('funcfoo', None)
196 195 self.paste(s)
197 196 nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult')
@@ -1,440 +1,440 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.core.ultratb
3 3 """
4 4 import io
5 5 import logging
6 6 import sys
7 7 import os.path
8 8 from textwrap import dedent
9 9 import traceback
10 10 import unittest
11 11 from unittest import mock
12 12
13 13 import IPython.core.ultratb as ultratb
14 14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
15 15
16 16
17 17 from IPython.testing import tools as tt
18 18 from IPython.testing.decorators import onlyif_unicode_paths
19 19 from IPython.utils.syspathcontext import prepended_to_syspath
20 20 from IPython.utils.tempdir import TemporaryDirectory
21 21
22 22 file_1 = """1
23 23 2
24 24 3
25 25 def f():
26 26 1/0
27 27 """
28 28
29 29 file_2 = """def f():
30 30 1/0
31 31 """
32 32
33 33
34 34 def recursionlimit(frames):
35 35 """
36 36 decorator to set the recursion limit temporarily
37 37 """
38 38
39 39 def inner(test_function):
40 40 def wrapper(*args, **kwargs):
41 41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
42 ultratb._FRAME_RECURSION_LIMIT = frames - 50
42 ultratb._FRAME_RECURSION_LIMIT = 50
43 43
44 44 rl = sys.getrecursionlimit()
45 45 sys.setrecursionlimit(frames)
46 46 try:
47 47 return test_function(*args, **kwargs)
48 48 finally:
49 49 sys.setrecursionlimit(rl)
50 50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
51 51
52 52 return wrapper
53 53
54 54 return inner
55 55
56 56
57 57 class ChangedPyFileTest(unittest.TestCase):
58 58 def test_changing_py_file(self):
59 59 """Traceback produced if the line where the error occurred is missing?
60 60
61 61 https://github.com/ipython/ipython/issues/1456
62 62 """
63 63 with TemporaryDirectory() as td:
64 64 fname = os.path.join(td, "foo.py")
65 65 with open(fname, "w") as f:
66 66 f.write(file_1)
67 67
68 68 with prepended_to_syspath(td):
69 69 ip.run_cell("import foo")
70 70
71 71 with tt.AssertPrints("ZeroDivisionError"):
72 72 ip.run_cell("foo.f()")
73 73
74 74 # Make the file shorter, so the line of the error is missing.
75 75 with open(fname, "w") as f:
76 76 f.write(file_2)
77 77
78 78 # For some reason, this was failing on the *second* call after
79 79 # changing the file, so we call f() twice.
80 80 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
81 81 with tt.AssertPrints("ZeroDivisionError"):
82 82 ip.run_cell("foo.f()")
83 83 with tt.AssertPrints("ZeroDivisionError"):
84 84 ip.run_cell("foo.f()")
85 85
86 86 iso_8859_5_file = u'''# coding: iso-8859-5
87 87
88 88 def fail():
89 89 """Π΄Π±Π˜Π–"""
90 90 1/0 # Π΄Π±Π˜Π–
91 91 '''
92 92
93 93 class NonAsciiTest(unittest.TestCase):
94 94 @onlyif_unicode_paths
95 95 def test_nonascii_path(self):
96 96 # Non-ascii directory name as well.
97 97 with TemporaryDirectory(suffix=u'Γ©') as td:
98 98 fname = os.path.join(td, u"fooΓ©.py")
99 99 with open(fname, "w") as f:
100 100 f.write(file_1)
101 101
102 102 with prepended_to_syspath(td):
103 103 ip.run_cell("import foo")
104 104
105 105 with tt.AssertPrints("ZeroDivisionError"):
106 106 ip.run_cell("foo.f()")
107 107
108 108 def test_iso8859_5(self):
109 109 with TemporaryDirectory() as td:
110 110 fname = os.path.join(td, 'dfghjkl.py')
111 111
112 112 with io.open(fname, 'w', encoding='iso-8859-5') as f:
113 113 f.write(iso_8859_5_file)
114 114
115 115 with prepended_to_syspath(td):
116 116 ip.run_cell("from dfghjkl import fail")
117 117
118 118 with tt.AssertPrints("ZeroDivisionError"):
119 119 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
120 120 ip.run_cell('fail()')
121 121
122 122 def test_nonascii_msg(self):
123 123 cell = u"raise Exception('Γ©')"
124 124 expected = u"Exception('Γ©')"
125 125 ip.run_cell("%xmode plain")
126 126 with tt.AssertPrints(expected):
127 127 ip.run_cell(cell)
128 128
129 129 ip.run_cell("%xmode verbose")
130 130 with tt.AssertPrints(expected):
131 131 ip.run_cell(cell)
132 132
133 133 ip.run_cell("%xmode context")
134 134 with tt.AssertPrints(expected):
135 135 ip.run_cell(cell)
136 136
137 137 ip.run_cell("%xmode minimal")
138 138 with tt.AssertPrints(u"Exception: Γ©"):
139 139 ip.run_cell(cell)
140 140
141 141 # Put this back into Context mode for later tests.
142 142 ip.run_cell("%xmode context")
143 143
144 144 class NestedGenExprTestCase(unittest.TestCase):
145 145 """
146 146 Regression test for the following issues:
147 147 https://github.com/ipython/ipython/issues/8293
148 148 https://github.com/ipython/ipython/issues/8205
149 149 """
150 150 def test_nested_genexpr(self):
151 151 code = dedent(
152 152 """\
153 153 class SpecificException(Exception):
154 154 pass
155 155
156 156 def foo(x):
157 157 raise SpecificException("Success!")
158 158
159 159 sum(sum(foo(x) for _ in [0]) for x in [0])
160 160 """
161 161 )
162 162 with tt.AssertPrints('SpecificException: Success!', suppress=False):
163 163 ip.run_cell(code)
164 164
165 165
166 166 indentationerror_file = """if True:
167 167 zoon()
168 168 """
169 169
170 170 class IndentationErrorTest(unittest.TestCase):
171 171 def test_indentationerror_shows_line(self):
172 172 # See issue gh-2398
173 173 with tt.AssertPrints("IndentationError"):
174 174 with tt.AssertPrints("zoon()", suppress=False):
175 175 ip.run_cell(indentationerror_file)
176 176
177 177 with TemporaryDirectory() as td:
178 178 fname = os.path.join(td, "foo.py")
179 179 with open(fname, "w") as f:
180 180 f.write(indentationerror_file)
181 181
182 182 with tt.AssertPrints("IndentationError"):
183 183 with tt.AssertPrints("zoon()", suppress=False):
184 184 ip.magic('run %s' % fname)
185 185
186 186 se_file_1 = """1
187 187 2
188 188 7/
189 189 """
190 190
191 191 se_file_2 = """7/
192 192 """
193 193
194 194 class SyntaxErrorTest(unittest.TestCase):
195 195 def test_syntaxerror_without_lineno(self):
196 196 with tt.AssertNotPrints("TypeError"):
197 197 with tt.AssertPrints("line unknown"):
198 198 ip.run_cell("raise SyntaxError()")
199 199
200 200 def test_syntaxerror_no_stacktrace_at_compile_time(self):
201 201 syntax_error_at_compile_time = """
202 202 def foo():
203 203 ..
204 204 """
205 205 with tt.AssertPrints("SyntaxError"):
206 206 ip.run_cell(syntax_error_at_compile_time)
207 207
208 208 with tt.AssertNotPrints("foo()"):
209 209 ip.run_cell(syntax_error_at_compile_time)
210 210
211 211 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
212 212 syntax_error_at_runtime = """
213 213 def foo():
214 214 eval("..")
215 215
216 216 def bar():
217 217 foo()
218 218
219 219 bar()
220 220 """
221 221 with tt.AssertPrints("SyntaxError"):
222 222 ip.run_cell(syntax_error_at_runtime)
223 223 # Assert syntax error during runtime generate stacktrace
224 224 with tt.AssertPrints(["foo()", "bar()"]):
225 225 ip.run_cell(syntax_error_at_runtime)
226 226 del ip.user_ns['bar']
227 227 del ip.user_ns['foo']
228 228
229 229 def test_changing_py_file(self):
230 230 with TemporaryDirectory() as td:
231 231 fname = os.path.join(td, "foo.py")
232 232 with open(fname, 'w') as f:
233 233 f.write(se_file_1)
234 234
235 235 with tt.AssertPrints(["7/", "SyntaxError"]):
236 236 ip.magic("run " + fname)
237 237
238 238 # Modify the file
239 239 with open(fname, 'w') as f:
240 240 f.write(se_file_2)
241 241
242 242 # The SyntaxError should point to the correct line
243 243 with tt.AssertPrints(["7/", "SyntaxError"]):
244 244 ip.magic("run " + fname)
245 245
246 246 def test_non_syntaxerror(self):
247 247 # SyntaxTB may be called with an error other than a SyntaxError
248 248 # See e.g. gh-4361
249 249 try:
250 250 raise ValueError('QWERTY')
251 251 except ValueError:
252 252 with tt.AssertPrints('QWERTY'):
253 253 ip.showsyntaxerror()
254 254
255 255
256 256 class Python3ChainedExceptionsTest(unittest.TestCase):
257 257 DIRECT_CAUSE_ERROR_CODE = """
258 258 try:
259 259 x = 1 + 2
260 260 print(not_defined_here)
261 261 except Exception as e:
262 262 x += 55
263 263 x - 1
264 264 y = {}
265 265 raise KeyError('uh') from e
266 266 """
267 267
268 268 EXCEPTION_DURING_HANDLING_CODE = """
269 269 try:
270 270 x = 1 + 2
271 271 print(not_defined_here)
272 272 except Exception as e:
273 273 x += 55
274 274 x - 1
275 275 y = {}
276 276 raise KeyError('uh')
277 277 """
278 278
279 279 SUPPRESS_CHAINING_CODE = """
280 280 try:
281 281 1/0
282 282 except Exception:
283 283 raise ValueError("Yikes") from None
284 284 """
285 285
286 286 def test_direct_cause_error(self):
287 287 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
288 288 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
289 289
290 290 def test_exception_during_handling_error(self):
291 291 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
292 292 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
293 293
294 294 def test_suppress_exception_chaining(self):
295 295 with tt.AssertNotPrints("ZeroDivisionError"), \
296 296 tt.AssertPrints("ValueError", suppress=False):
297 297 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
298 298
299 299
300 300 class RecursionTest(unittest.TestCase):
301 301 DEFINITIONS = """
302 302 def non_recurs():
303 303 1/0
304 304
305 305 def r1():
306 306 r1()
307 307
308 308 def r3a():
309 309 r3b()
310 310
311 311 def r3b():
312 312 r3c()
313 313
314 314 def r3c():
315 315 r3a()
316 316
317 317 def r3o1():
318 318 r3a()
319 319
320 320 def r3o2():
321 321 r3o1()
322 322 """
323 323 def setUp(self):
324 324 ip.run_cell(self.DEFINITIONS)
325 325
326 326 def test_no_recursion(self):
327 327 with tt.AssertNotPrints("frames repeated"):
328 328 ip.run_cell("non_recurs()")
329 329
330 @recursionlimit(65)
330 @recursionlimit(150)
331 331 def test_recursion_one_frame(self):
332 332 with tt.AssertPrints("1 frames repeated"):
333 333 ip.run_cell("r1()")
334 334
335 @recursionlimit(65)
335 @recursionlimit(150)
336 336 def test_recursion_three_frames(self):
337 337 with tt.AssertPrints("3 frames repeated"):
338 338 ip.run_cell("r3o2()")
339 339
340 @recursionlimit(65)
340 @recursionlimit(150)
341 341 def test_find_recursion(self):
342 342 captured = []
343 343 def capture_exc(*args, **kwargs):
344 344 captured.append(sys.exc_info())
345 345 with mock.patch.object(ip, 'showtraceback', capture_exc):
346 346 ip.run_cell("r3o2()")
347 347
348 348 self.assertEqual(len(captured), 1)
349 349 etype, evalue, tb = captured[0]
350 350 self.assertIn("recursion", str(evalue))
351 351
352 352 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
353 353 for r in records[:10]:
354 354 print(r[1:4])
355 355
356 356 # The outermost frames should be:
357 357 # 0: the 'cell' that was running when the exception came up
358 358 # 1: r3o2()
359 359 # 2: r3o1()
360 360 # 3: r3a()
361 361 # Then repeating r3b, r3c, r3a
362 362 last_unique, repeat_length = find_recursion(etype, evalue, records)
363 363 self.assertEqual(last_unique, 2)
364 364 self.assertEqual(repeat_length, 3)
365 365
366 366
367 367 #----------------------------------------------------------------------------
368 368
369 369 # module testing (minimal)
370 370 def test_handlers():
371 371 def spam(c, d_e):
372 372 (d, e) = d_e
373 373 x = c + d
374 374 y = c * d
375 375 foo(x, y)
376 376
377 377 def foo(a, b, bar=1):
378 378 eggs(a, b + bar)
379 379
380 380 def eggs(f, g, z=globals()):
381 381 h = f + g
382 382 i = f - g
383 383 return h / i
384 384
385 385 buff = io.StringIO()
386 386
387 387 buff.write('')
388 388 buff.write('*** Before ***')
389 389 try:
390 390 buff.write(spam(1, (2, 3)))
391 391 except:
392 392 traceback.print_exc(file=buff)
393 393
394 394 handler = ColorTB(ostream=buff)
395 395 buff.write('*** ColorTB ***')
396 396 try:
397 397 buff.write(spam(1, (2, 3)))
398 398 except:
399 399 handler(*sys.exc_info())
400 400 buff.write('')
401 401
402 402 handler = VerboseTB(ostream=buff)
403 403 buff.write('*** VerboseTB ***')
404 404 try:
405 405 buff.write(spam(1, (2, 3)))
406 406 except:
407 407 handler(*sys.exc_info())
408 408 buff.write('')
409 409
410 410 from IPython.testing.decorators import skipif
411 411
412 412 class TokenizeFailureTest(unittest.TestCase):
413 413 """Tests related to https://github.com/ipython/ipython/issues/6864."""
414 414
415 415 # that appear to test that we are handling an exception that can be thrown
416 416 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
417 417 # I'm unsure if other sequences can make it raise this error. Let's just
418 418 # skip in 3.8 for now
419 419 @skipif(sys.version_info > (3,8))
420 420 def testLogging(self):
421 421 message = "An unexpected error occurred while tokenizing input"
422 422 cell = 'raise ValueError("""a\nb""")'
423 423
424 424 stream = io.StringIO()
425 425 handler = logging.StreamHandler(stream)
426 426 logger = logging.getLogger()
427 427 loglevel = logger.level
428 428 logger.addHandler(handler)
429 429 self.addCleanup(lambda: logger.removeHandler(handler))
430 430 self.addCleanup(lambda: logger.setLevel(loglevel))
431 431
432 432 logger.setLevel(logging.INFO)
433 433 with tt.AssertNotPrints(message):
434 434 ip.run_cell(cell)
435 435 self.assertNotIn(message, stream.getvalue())
436 436
437 437 logger.setLevel(logging.DEBUG)
438 438 with tt.AssertNotPrints(message):
439 439 ip.run_cell(cell)
440 440 self.assertIn(message, stream.getvalue())
@@ -1,133 +1,133 b''
1 1 """Tests for tokenutil"""
2 2 # Copyright (c) IPython Development Team.
3 3 # Distributed under the terms of the Modified BSD License.
4 4
5 5 import nose.tools as nt
6 6
7 7 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
8 8
9 9 def expect_token(expected, cell, cursor_pos):
10 10 token = token_at_cursor(cell, cursor_pos)
11 11 offset = 0
12 12 for line in cell.splitlines():
13 13 if offset + len(line) >= cursor_pos:
14 14 break
15 15 else:
16 16 offset += len(line)+1
17 17 column = cursor_pos - offset
18 18 line_with_cursor = '%s|%s' % (line[:column], line[column:])
19 19 nt.assert_equal(token, expected,
20 20 "Expected %r, got %r in: %r (pos %i)" % (
21 21 expected, token, line_with_cursor, cursor_pos)
22 22 )
23 23
24 24 def test_simple():
25 25 cell = "foo"
26 26 for i in range(len(cell)):
27 27 expect_token("foo", cell, i)
28 28
29 29 def test_function():
30 30 cell = "foo(a=5, b='10')"
31 31 expected = 'foo'
32 32 # up to `foo(|a=`
33 33 for i in range(cell.find('a=') + 1):
34 34 expect_token("foo", cell, i)
35 35 # find foo after `=`
36 36 for i in [cell.find('=') + 1, cell.rfind('=') + 1]:
37 37 expect_token("foo", cell, i)
38 38 # in between `5,|` and `|b=`
39 39 for i in range(cell.find(','), cell.find('b=')):
40 40 expect_token("foo", cell, i)
41 41
42 42 def test_multiline():
43 43 cell = '\n'.join([
44 44 'a = 5',
45 45 'b = hello("string", there)'
46 46 ])
47 47 expected = 'hello'
48 48 start = cell.index(expected) + 1
49 49 for i in range(start, start + len(expected)):
50 50 expect_token(expected, cell, i)
51 51 expected = 'hello'
52 52 start = cell.index(expected) + 1
53 53 for i in range(start, start + len(expected)):
54 54 expect_token(expected, cell, i)
55 55
56 56 def test_multiline_token():
57 57 cell = '\n'.join([
58 58 '"""\n\nxxxxxxxxxx\n\n"""',
59 59 '5, """',
60 60 'docstring',
61 61 'multiline token',
62 62 '""", [',
63 63 '2, 3, "complicated"]',
64 64 'b = hello("string", there)'
65 65 ])
66 66 expected = 'hello'
67 67 start = cell.index(expected) + 1
68 68 for i in range(start, start + len(expected)):
69 69 expect_token(expected, cell, i)
70 70 expected = 'hello'
71 71 start = cell.index(expected) + 1
72 72 for i in range(start, start + len(expected)):
73 73 expect_token(expected, cell, i)
74 74
75 75 def test_nested_call():
76 76 cell = "foo(bar(a=5), b=10)"
77 77 expected = 'foo'
78 78 start = cell.index('bar') + 1
79 79 for i in range(start, start + 3):
80 80 expect_token(expected, cell, i)
81 81 expected = 'bar'
82 82 start = cell.index('a=')
83 83 for i in range(start, start + 3):
84 84 expect_token(expected, cell, i)
85 85 expected = 'foo'
86 86 start = cell.index(')') + 1
87 87 for i in range(start, len(cell)-1):
88 88 expect_token(expected, cell, i)
89 89
90 90 def test_attrs():
91 91 cell = "a = obj.attr.subattr"
92 92 expected = 'obj'
93 93 idx = cell.find('obj') + 1
94 94 for i in range(idx, idx + 3):
95 95 expect_token(expected, cell, i)
96 96 idx = cell.find('.attr') + 2
97 97 expected = 'obj.attr'
98 98 for i in range(idx, idx + 4):
99 99 expect_token(expected, cell, i)
100 100 idx = cell.find('.subattr') + 2
101 101 expected = 'obj.attr.subattr'
102 102 for i in range(idx, len(cell)):
103 103 expect_token(expected, cell, i)
104 104
105 105 def test_line_at_cursor():
106 106 cell = ""
107 107 (line, offset) = line_at_cursor(cell, cursor_pos=11)
108 108 nt.assert_equal(line, "")
109 109 nt.assert_equal(offset, 0)
110 110
111 111 # The position after a newline should be the start of the following line.
112 112 cell = "One\nTwo\n"
113 113 (line, offset) = line_at_cursor(cell, cursor_pos=4)
114 114 nt.assert_equal(line, "Two\n")
115 115 nt.assert_equal(offset, 4)
116 116
117 117 # The end of a cell should be on the last line
118 118 cell = "pri\npri"
119 119 (line, offset) = line_at_cursor(cell, cursor_pos=7)
120 120 nt.assert_equal(line, "pri")
121 121 nt.assert_equal(offset, 4)
122 122
123 123 def test_muliline_statement():
124 124 cell = """a = (1,
125 125 3)
126 126
127 127 int()
128 128 map()
129 129 """
130 130 for c in range(16, 22):
131 yield lambda: expect_token("int", cell, c)
131 yield lambda cell, c: expect_token("int", cell, c), cell, c
132 132 for c in range(22, 28):
133 yield lambda: expect_token("map", cell, c)
133 yield lambda cell, c: expect_token("map", cell, c), cell, c
General Comments 0
You need to be logged in to leave comments. Login now