1"""Test cases for traceback module""" 2 3from collections import namedtuple 4from io import StringIO 5import linecache 6import sys 7import unittest 8import re 9from test import support 10from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ 11from test.support.script_helper import assert_python_ok 12import textwrap 13 14import traceback 15 16 17test_code = namedtuple('code', ['co_filename', 'co_name']) 18test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals']) 19test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next']) 20 21 22class TracebackCases(unittest.TestCase): 23 # For now, a very minimal set of tests. I want to be sure that 24 # formatting of SyntaxErrors works based on changes for 2.1. 25 26 def get_exception_format(self, func, exc): 27 try: 28 func() 29 except exc as value: 30 return traceback.format_exception_only(exc, value) 31 else: 32 raise ValueError("call did not raise exception") 33 34 def syntax_error_with_caret(self): 35 compile("def fact(x):\n\treturn x!\n", "?", "exec") 36 37 def syntax_error_with_caret_2(self): 38 compile("1 +\n", "?", "exec") 39 40 def syntax_error_bad_indentation(self): 41 compile("def spam():\n print(1)\n print(2)", "?", "exec") 42 43 def syntax_error_with_caret_non_ascii(self): 44 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec") 45 46 def syntax_error_bad_indentation2(self): 47 compile(" print(2)", "?", "exec") 48 49 def test_caret(self): 50 err = self.get_exception_format(self.syntax_error_with_caret, 51 SyntaxError) 52 self.assertEqual(len(err), 4) 53 self.assertTrue(err[1].strip() == "return x!") 54 self.assertIn("^", err[2]) # third line has caret 55 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place 56 57 err = self.get_exception_format(self.syntax_error_with_caret_2, 58 SyntaxError) 59 self.assertIn("^", err[2]) # third line has caret 60 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 61 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place 62 63 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii, 64 SyntaxError) 65 self.assertIn("^", err[2]) # third line has caret 66 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 67 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place 68 69 def test_nocaret(self): 70 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) 71 err = traceback.format_exception_only(SyntaxError, exc) 72 self.assertEqual(len(err), 3) 73 self.assertEqual(err[1].strip(), "bad syntax") 74 75 def test_bad_indentation(self): 76 err = self.get_exception_format(self.syntax_error_bad_indentation, 77 IndentationError) 78 self.assertEqual(len(err), 4) 79 self.assertEqual(err[1].strip(), "print(2)") 80 self.assertIn("^", err[2]) 81 self.assertEqual(err[1].find(")") + 1, err[2].find("^")) 82 83 # No caret for "unexpected indent" 84 err = self.get_exception_format(self.syntax_error_bad_indentation2, 85 IndentationError) 86 self.assertEqual(len(err), 3) 87 self.assertEqual(err[1].strip(), "print(2)") 88 89 def test_base_exception(self): 90 # Test that exceptions derived from BaseException are formatted right 91 e = KeyboardInterrupt() 92 lst = traceback.format_exception_only(e.__class__, e) 93 self.assertEqual(lst, ['KeyboardInterrupt\n']) 94 95 def test_format_exception_only_bad__str__(self): 96 class X(Exception): 97 def __str__(self): 98 1/0 99 err = traceback.format_exception_only(X, X()) 100 self.assertEqual(len(err), 1) 101 str_value = '<unprintable %s object>' % X.__name__ 102 if X.__module__ in ('__main__', 'builtins'): 103 str_name = X.__qualname__ 104 else: 105 str_name = '.'.join([X.__module__, X.__qualname__]) 106 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) 107 108 def test_encoded_file(self): 109 # Test that tracebacks are correctly printed for encoded source files: 110 # - correct line number (Issue2384) 111 # - respect file encoding (Issue3975) 112 import sys, subprocess 113 114 # The spawned subprocess has its stdout redirected to a PIPE, and its 115 # encoding may be different from the current interpreter, on Windows 116 # at least. 117 process = subprocess.Popen([sys.executable, "-c", 118 "import sys; print(sys.stdout.encoding)"], 119 stdout=subprocess.PIPE, 120 stderr=subprocess.STDOUT) 121 stdout, stderr = process.communicate() 122 output_encoding = str(stdout, 'ascii').splitlines()[0] 123 124 def do_test(firstlines, message, charset, lineno): 125 # Raise the message in a subprocess, and catch the output 126 try: 127 with open(TESTFN, "w", encoding=charset) as output: 128 output.write("""{0}if 1: 129 import traceback; 130 raise RuntimeError('{1}') 131 """.format(firstlines, message)) 132 133 process = subprocess.Popen([sys.executable, TESTFN], 134 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 135 stdout, stderr = process.communicate() 136 stdout = stdout.decode(output_encoding).splitlines() 137 finally: 138 unlink(TESTFN) 139 140 # The source lines are encoded with the 'backslashreplace' handler 141 encoded_message = message.encode(output_encoding, 142 'backslashreplace') 143 # and we just decoded them with the output_encoding. 144 message_ascii = encoded_message.decode(output_encoding) 145 146 err_line = "raise RuntimeError('{0}')".format(message_ascii) 147 err_msg = "RuntimeError: {0}".format(message_ascii) 148 149 self.assertIn(("line %s" % lineno), stdout[1], 150 "Invalid line number: {0!r} instead of {1}".format( 151 stdout[1], lineno)) 152 self.assertTrue(stdout[2].endswith(err_line), 153 "Invalid traceback line: {0!r} instead of {1!r}".format( 154 stdout[2], err_line)) 155 self.assertTrue(stdout[3] == err_msg, 156 "Invalid error message: {0!r} instead of {1!r}".format( 157 stdout[3], err_msg)) 158 159 do_test("", "foo", "ascii", 3) 160 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"): 161 if charset == "ascii": 162 text = "foo" 163 elif charset == "GBK": 164 text = "\u4E02\u5100" 165 else: 166 text = "h\xe9 ho" 167 do_test("# coding: {0}\n".format(charset), 168 text, charset, 4) 169 do_test("#!shebang\n# coding: {0}\n".format(charset), 170 text, charset, 5) 171 do_test(" \t\f\n# coding: {0}\n".format(charset), 172 text, charset, 5) 173 # Issue #18960: coding spec should have no effect 174 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) 175 176 def test_print_traceback_at_exit(self): 177 # Issue #22599: Ensure that it is possible to use the traceback module 178 # to display an exception at Python exit 179 code = textwrap.dedent(""" 180 import sys 181 import traceback 182 183 class PrintExceptionAtExit(object): 184 def __init__(self): 185 try: 186 x = 1 / 0 187 except Exception: 188 self.exc_info = sys.exc_info() 189 # self.exc_info[1] (traceback) contains frames: 190 # explicitly clear the reference to self in the current 191 # frame to break a reference cycle 192 self = None 193 194 def __del__(self): 195 traceback.print_exception(*self.exc_info) 196 197 # Keep a reference in the module namespace to call the destructor 198 # when the module is unloaded 199 obj = PrintExceptionAtExit() 200 """) 201 rc, stdout, stderr = assert_python_ok('-c', code) 202 expected = [b'Traceback (most recent call last):', 203 b' File "<string>", line 8, in __init__', 204 b'ZeroDivisionError: division by zero'] 205 self.assertEqual(stderr.splitlines(), expected) 206 207 def test_print_exception(self): 208 output = StringIO() 209 traceback.print_exception( 210 Exception, Exception("projector"), None, file=output 211 ) 212 self.assertEqual(output.getvalue(), "Exception: projector\n") 213 214 215class TracebackFormatTests(unittest.TestCase): 216 217 def some_exception(self): 218 raise KeyError('blah') 219 220 @cpython_only 221 def check_traceback_format(self, cleanup_func=None): 222 from _testcapi import traceback_print 223 try: 224 self.some_exception() 225 except KeyError: 226 type_, value, tb = sys.exc_info() 227 if cleanup_func is not None: 228 # Clear the inner frames, not this one 229 cleanup_func(tb.tb_next) 230 traceback_fmt = 'Traceback (most recent call last):\n' + \ 231 ''.join(traceback.format_tb(tb)) 232 file_ = StringIO() 233 traceback_print(tb, file_) 234 python_fmt = file_.getvalue() 235 # Call all _tb and _exc functions 236 with captured_output("stderr") as tbstderr: 237 traceback.print_tb(tb) 238 tbfile = StringIO() 239 traceback.print_tb(tb, file=tbfile) 240 with captured_output("stderr") as excstderr: 241 traceback.print_exc() 242 excfmt = traceback.format_exc() 243 excfile = StringIO() 244 traceback.print_exc(file=excfile) 245 else: 246 raise Error("unable to create test traceback string") 247 248 # Make sure that Python and the traceback module format the same thing 249 self.assertEqual(traceback_fmt, python_fmt) 250 # Now verify the _tb func output 251 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue()) 252 # Now verify the _exc func output 253 self.assertEqual(excstderr.getvalue(), excfile.getvalue()) 254 self.assertEqual(excfmt, excfile.getvalue()) 255 256 # Make sure that the traceback is properly indented. 257 tb_lines = python_fmt.splitlines() 258 self.assertEqual(len(tb_lines), 5) 259 banner = tb_lines[0] 260 location, source_line = tb_lines[-2:] 261 self.assertTrue(banner.startswith('Traceback')) 262 self.assertTrue(location.startswith(' File')) 263 self.assertTrue(source_line.startswith(' raise')) 264 265 def test_traceback_format(self): 266 self.check_traceback_format() 267 268 def test_traceback_format_with_cleared_frames(self): 269 # Check that traceback formatting also works with a clear()ed frame 270 def cleanup_tb(tb): 271 tb.tb_frame.clear() 272 self.check_traceback_format(cleanup_tb) 273 274 def test_stack_format(self): 275 # Verify _stack functions. Note we have to use _getframe(1) to 276 # compare them without this frame appearing in the output 277 with captured_output("stderr") as ststderr: 278 traceback.print_stack(sys._getframe(1)) 279 stfile = StringIO() 280 traceback.print_stack(sys._getframe(1), file=stfile) 281 self.assertEqual(ststderr.getvalue(), stfile.getvalue()) 282 283 stfmt = traceback.format_stack(sys._getframe(1)) 284 285 self.assertEqual(ststderr.getvalue(), "".join(stfmt)) 286 287 def test_print_stack(self): 288 def prn(): 289 traceback.print_stack() 290 with captured_output("stderr") as stderr: 291 prn() 292 lineno = prn.__code__.co_firstlineno 293 self.assertEqual(stderr.getvalue().splitlines()[-4:], [ 294 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3), 295 ' prn()', 296 ' File "%s", line %d, in prn' % (__file__, lineno+1), 297 ' traceback.print_stack()', 298 ]) 299 300 # issue 26823 - Shrink recursive tracebacks 301 def _check_recursive_traceback_display(self, render_exc): 302 # Always show full diffs when this test fails 303 # Note that rearranging things may require adjusting 304 # the relative line numbers in the expected tracebacks 305 self.maxDiff = None 306 307 # Check hitting the recursion limit 308 def f(): 309 f() 310 311 with captured_output("stderr") as stderr_f: 312 try: 313 f() 314 except RecursionError: 315 render_exc() 316 else: 317 self.fail("no recursion occurred") 318 319 lineno_f = f.__code__.co_firstlineno 320 result_f = ( 321 'Traceback (most recent call last):\n' 322 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' 323 ' f()\n' 324 f' File "{__file__}", line {lineno_f+1}, in f\n' 325 ' f()\n' 326 f' File "{__file__}", line {lineno_f+1}, in f\n' 327 ' f()\n' 328 f' File "{__file__}", line {lineno_f+1}, in f\n' 329 ' f()\n' 330 # XXX: The following line changes depending on whether the tests 331 # are run through the interactive interpreter or with -m 332 # It also varies depending on the platform (stack size) 333 # Fortunately, we don't care about exactness here, so we use regex 334 r' \[Previous line repeated (\d+) more times\]' '\n' 335 'RecursionError: maximum recursion depth exceeded\n' 336 ) 337 338 expected = result_f.splitlines() 339 actual = stderr_f.getvalue().splitlines() 340 341 # Check the output text matches expectations 342 # 2nd last line contains the repetition count 343 self.assertEqual(actual[:-2], expected[:-2]) 344 self.assertRegex(actual[-2], expected[-2]) 345 # last line can have additional text appended 346 self.assertIn(expected[-1], actual[-1]) 347 348 # Check the recursion count is roughly as expected 349 rec_limit = sys.getrecursionlimit() 350 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit)) 351 352 # Check a known (limited) number of recursive invocations 353 def g(count=10): 354 if count: 355 return g(count-1) 356 raise ValueError 357 358 with captured_output("stderr") as stderr_g: 359 try: 360 g() 361 except ValueError: 362 render_exc() 363 else: 364 self.fail("no value error was raised") 365 366 lineno_g = g.__code__.co_firstlineno 367 result_g = ( 368 f' File "{__file__}", line {lineno_g+2}, in g\n' 369 ' return g(count-1)\n' 370 f' File "{__file__}", line {lineno_g+2}, in g\n' 371 ' return g(count-1)\n' 372 f' File "{__file__}", line {lineno_g+2}, in g\n' 373 ' return g(count-1)\n' 374 ' [Previous line repeated 7 more times]\n' 375 f' File "{__file__}", line {lineno_g+3}, in g\n' 376 ' raise ValueError\n' 377 'ValueError\n' 378 ) 379 tb_line = ( 380 'Traceback (most recent call last):\n' 381 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' 382 ' g()\n' 383 ) 384 expected = (tb_line + result_g).splitlines() 385 actual = stderr_g.getvalue().splitlines() 386 self.assertEqual(actual, expected) 387 388 # Check 2 different repetitive sections 389 def h(count=10): 390 if count: 391 return h(count-1) 392 g() 393 394 with captured_output("stderr") as stderr_h: 395 try: 396 h() 397 except ValueError: 398 render_exc() 399 else: 400 self.fail("no value error was raised") 401 402 lineno_h = h.__code__.co_firstlineno 403 result_h = ( 404 'Traceback (most recent call last):\n' 405 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' 406 ' h()\n' 407 f' File "{__file__}", line {lineno_h+2}, in h\n' 408 ' return h(count-1)\n' 409 f' File "{__file__}", line {lineno_h+2}, in h\n' 410 ' return h(count-1)\n' 411 f' File "{__file__}", line {lineno_h+2}, in h\n' 412 ' return h(count-1)\n' 413 ' [Previous line repeated 7 more times]\n' 414 f' File "{__file__}", line {lineno_h+3}, in h\n' 415 ' g()\n' 416 ) 417 expected = (result_h + result_g).splitlines() 418 actual = stderr_h.getvalue().splitlines() 419 self.assertEqual(actual, expected) 420 421 # Check the boundary conditions. First, test just below the cutoff. 422 with captured_output("stderr") as stderr_g: 423 try: 424 g(traceback._RECURSIVE_CUTOFF) 425 except ValueError: 426 render_exc() 427 else: 428 self.fail("no error raised") 429 result_g = ( 430 f' File "{__file__}", line {lineno_g+2}, in g\n' 431 ' return g(count-1)\n' 432 f' File "{__file__}", line {lineno_g+2}, in g\n' 433 ' return g(count-1)\n' 434 f' File "{__file__}", line {lineno_g+2}, in g\n' 435 ' return g(count-1)\n' 436 f' File "{__file__}", line {lineno_g+3}, in g\n' 437 ' raise ValueError\n' 438 'ValueError\n' 439 ) 440 tb_line = ( 441 'Traceback (most recent call last):\n' 442 f' File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n' 443 ' g(traceback._RECURSIVE_CUTOFF)\n' 444 ) 445 expected = (tb_line + result_g).splitlines() 446 actual = stderr_g.getvalue().splitlines() 447 self.assertEqual(actual, expected) 448 449 # Second, test just above the cutoff. 450 with captured_output("stderr") as stderr_g: 451 try: 452 g(traceback._RECURSIVE_CUTOFF + 1) 453 except ValueError: 454 render_exc() 455 else: 456 self.fail("no error raised") 457 result_g = ( 458 f' File "{__file__}", line {lineno_g+2}, in g\n' 459 ' return g(count-1)\n' 460 f' File "{__file__}", line {lineno_g+2}, in g\n' 461 ' return g(count-1)\n' 462 f' File "{__file__}", line {lineno_g+2}, in g\n' 463 ' return g(count-1)\n' 464 ' [Previous line repeated 1 more time]\n' 465 f' File "{__file__}", line {lineno_g+3}, in g\n' 466 ' raise ValueError\n' 467 'ValueError\n' 468 ) 469 tb_line = ( 470 'Traceback (most recent call last):\n' 471 f' File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n' 472 ' g(traceback._RECURSIVE_CUTOFF + 1)\n' 473 ) 474 expected = (tb_line + result_g).splitlines() 475 actual = stderr_g.getvalue().splitlines() 476 self.assertEqual(actual, expected) 477 478 def test_recursive_traceback_python(self): 479 self._check_recursive_traceback_display(traceback.print_exc) 480 481 @cpython_only 482 def test_recursive_traceback_cpython_internal(self): 483 from _testcapi import exception_print 484 def render_exc(): 485 exc_type, exc_value, exc_tb = sys.exc_info() 486 exception_print(exc_value) 487 self._check_recursive_traceback_display(render_exc) 488 489 def test_format_stack(self): 490 def fmt(): 491 return traceback.format_stack() 492 result = fmt() 493 lineno = fmt.__code__.co_firstlineno 494 self.assertEqual(result[-2:], [ 495 ' File "%s", line %d, in test_format_stack\n' 496 ' result = fmt()\n' % (__file__, lineno+2), 497 ' File "%s", line %d, in fmt\n' 498 ' return traceback.format_stack()\n' % (__file__, lineno+1), 499 ]) 500 501 @cpython_only 502 def test_unhashable(self): 503 from _testcapi import exception_print 504 505 class UnhashableException(Exception): 506 def __eq__(self, other): 507 return True 508 509 ex1 = UnhashableException('ex1') 510 ex2 = UnhashableException('ex2') 511 try: 512 raise ex2 from ex1 513 except UnhashableException: 514 try: 515 raise ex1 516 except UnhashableException: 517 exc_type, exc_val, exc_tb = sys.exc_info() 518 519 with captured_output("stderr") as stderr_f: 520 exception_print(exc_val) 521 522 tb = stderr_f.getvalue().strip().splitlines() 523 self.assertEqual(11, len(tb)) 524 self.assertEqual(context_message.strip(), tb[5]) 525 self.assertIn('UnhashableException: ex2', tb[3]) 526 self.assertIn('UnhashableException: ex1', tb[10]) 527 528 529cause_message = ( 530 "\nThe above exception was the direct cause " 531 "of the following exception:\n\n") 532 533context_message = ( 534 "\nDuring handling of the above exception, " 535 "another exception occurred:\n\n") 536 537boundaries = re.compile( 538 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message))) 539 540 541class BaseExceptionReportingTests: 542 543 def get_exception(self, exception_or_callable): 544 if isinstance(exception_or_callable, Exception): 545 return exception_or_callable 546 try: 547 exception_or_callable() 548 except Exception as e: 549 return e 550 551 def zero_div(self): 552 1/0 # In zero_div 553 554 def check_zero_div(self, msg): 555 lines = msg.splitlines() 556 self.assertTrue(lines[-3].startswith(' File')) 557 self.assertIn('1/0 # In zero_div', lines[-2]) 558 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1]) 559 560 def test_simple(self): 561 try: 562 1/0 # Marker 563 except ZeroDivisionError as _: 564 e = _ 565 lines = self.get_report(e).splitlines() 566 self.assertEqual(len(lines), 4) 567 self.assertTrue(lines[0].startswith('Traceback')) 568 self.assertTrue(lines[1].startswith(' File')) 569 self.assertIn('1/0 # Marker', lines[2]) 570 self.assertTrue(lines[3].startswith('ZeroDivisionError')) 571 572 def test_cause(self): 573 def inner_raise(): 574 try: 575 self.zero_div() 576 except ZeroDivisionError as e: 577 raise KeyError from e 578 def outer_raise(): 579 inner_raise() # Marker 580 blocks = boundaries.split(self.get_report(outer_raise)) 581 self.assertEqual(len(blocks), 3) 582 self.assertEqual(blocks[1], cause_message) 583 self.check_zero_div(blocks[0]) 584 self.assertIn('inner_raise() # Marker', blocks[2]) 585 586 def test_context(self): 587 def inner_raise(): 588 try: 589 self.zero_div() 590 except ZeroDivisionError: 591 raise KeyError 592 def outer_raise(): 593 inner_raise() # Marker 594 blocks = boundaries.split(self.get_report(outer_raise)) 595 self.assertEqual(len(blocks), 3) 596 self.assertEqual(blocks[1], context_message) 597 self.check_zero_div(blocks[0]) 598 self.assertIn('inner_raise() # Marker', blocks[2]) 599 600 def test_context_suppression(self): 601 try: 602 try: 603 raise Exception 604 except: 605 raise ZeroDivisionError from None 606 except ZeroDivisionError as _: 607 e = _ 608 lines = self.get_report(e).splitlines() 609 self.assertEqual(len(lines), 4) 610 self.assertTrue(lines[0].startswith('Traceback')) 611 self.assertTrue(lines[1].startswith(' File')) 612 self.assertIn('ZeroDivisionError from None', lines[2]) 613 self.assertTrue(lines[3].startswith('ZeroDivisionError')) 614 615 def test_cause_and_context(self): 616 # When both a cause and a context are set, only the cause should be 617 # displayed and the context should be muted. 618 def inner_raise(): 619 try: 620 self.zero_div() 621 except ZeroDivisionError as _e: 622 e = _e 623 try: 624 xyzzy 625 except NameError: 626 raise KeyError from e 627 def outer_raise(): 628 inner_raise() # Marker 629 blocks = boundaries.split(self.get_report(outer_raise)) 630 self.assertEqual(len(blocks), 3) 631 self.assertEqual(blocks[1], cause_message) 632 self.check_zero_div(blocks[0]) 633 self.assertIn('inner_raise() # Marker', blocks[2]) 634 635 def test_cause_recursive(self): 636 def inner_raise(): 637 try: 638 try: 639 self.zero_div() 640 except ZeroDivisionError as e: 641 z = e 642 raise KeyError from e 643 except KeyError as e: 644 raise z from e 645 def outer_raise(): 646 inner_raise() # Marker 647 blocks = boundaries.split(self.get_report(outer_raise)) 648 self.assertEqual(len(blocks), 3) 649 self.assertEqual(blocks[1], cause_message) 650 # The first block is the KeyError raised from the ZeroDivisionError 651 self.assertIn('raise KeyError from e', blocks[0]) 652 self.assertNotIn('1/0', blocks[0]) 653 # The second block (apart from the boundary) is the ZeroDivisionError 654 # re-raised from the KeyError 655 self.assertIn('inner_raise() # Marker', blocks[2]) 656 self.check_zero_div(blocks[2]) 657 658 @unittest.skipIf(support.use_old_parser(), "Pegen is arguably better here, so no need to fix this") 659 def test_syntax_error_offset_at_eol(self): 660 # See #10186. 661 def e(): 662 raise SyntaxError('', ('', 0, 5, 'hello')) 663 msg = self.get_report(e).splitlines() 664 self.assertEqual(msg[-2], " ^") 665 def e(): 666 exec("x = 5 | 4 |") 667 msg = self.get_report(e).splitlines() 668 self.assertEqual(msg[-2], ' ^') 669 670 def test_message_none(self): 671 # A message that looks like "None" should not be treated specially 672 err = self.get_report(Exception(None)) 673 self.assertIn('Exception: None\n', err) 674 err = self.get_report(Exception('None')) 675 self.assertIn('Exception: None\n', err) 676 err = self.get_report(Exception()) 677 self.assertIn('Exception\n', err) 678 err = self.get_report(Exception('')) 679 self.assertIn('Exception\n', err) 680 681 def test_syntax_error_various_offsets(self): 682 for offset in range(-5, 10): 683 for add in [0, 2]: 684 text = " "*add + "text%d" % offset 685 expected = [' File "file.py", line 1'] 686 if offset < 1: 687 expected.append(" %s" % text.lstrip()) 688 elif offset <= 6: 689 expected.append(" %s" % text.lstrip()) 690 expected.append(" %s^" % (" "*(offset-1))) 691 else: 692 expected.append(" %s" % text.lstrip()) 693 expected.append(" %s^" % (" "*5)) 694 expected.append("SyntaxError: msg") 695 expected.append("") 696 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text))) 697 exp = "\n".join(expected) 698 self.assertEqual(exp, err) 699 700 701class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): 702 # 703 # This checks reporting through the 'traceback' module, with both 704 # format_exception() and print_exception(). 705 # 706 707 def get_report(self, e): 708 e = self.get_exception(e) 709 s = ''.join( 710 traceback.format_exception(type(e), e, e.__traceback__)) 711 with captured_output("stderr") as sio: 712 traceback.print_exception(type(e), e, e.__traceback__) 713 self.assertEqual(sio.getvalue(), s) 714 return s 715 716 717class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): 718 # 719 # This checks built-in reporting by the interpreter. 720 # 721 722 @cpython_only 723 def get_report(self, e): 724 from _testcapi import exception_print 725 e = self.get_exception(e) 726 with captured_output("stderr") as s: 727 exception_print(e) 728 return s.getvalue() 729 730 731class LimitTests(unittest.TestCase): 732 733 ''' Tests for limit argument. 734 It's enough to test extact_tb, extract_stack and format_exception ''' 735 736 def last_raises1(self): 737 raise Exception('Last raised') 738 739 def last_raises2(self): 740 self.last_raises1() 741 742 def last_raises3(self): 743 self.last_raises2() 744 745 def last_raises4(self): 746 self.last_raises3() 747 748 def last_raises5(self): 749 self.last_raises4() 750 751 def last_returns_frame1(self): 752 return sys._getframe() 753 754 def last_returns_frame2(self): 755 return self.last_returns_frame1() 756 757 def last_returns_frame3(self): 758 return self.last_returns_frame2() 759 760 def last_returns_frame4(self): 761 return self.last_returns_frame3() 762 763 def last_returns_frame5(self): 764 return self.last_returns_frame4() 765 766 def test_extract_stack(self): 767 frame = self.last_returns_frame5() 768 def extract(**kwargs): 769 return traceback.extract_stack(frame, **kwargs) 770 def assertEqualExcept(actual, expected, ignore): 771 self.assertEqual(actual[:ignore], expected[:ignore]) 772 self.assertEqual(actual[ignore+1:], expected[ignore+1:]) 773 self.assertEqual(len(actual), len(expected)) 774 775 with support.swap_attr(sys, 'tracebacklimit', 1000): 776 nolim = extract() 777 self.assertGreater(len(nolim), 5) 778 self.assertEqual(extract(limit=2), nolim[-2:]) 779 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1) 780 self.assertEqual(extract(limit=-2), nolim[:2]) 781 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1) 782 self.assertEqual(extract(limit=0), []) 783 del sys.tracebacklimit 784 assertEqualExcept(extract(), nolim, -5-1) 785 sys.tracebacklimit = 2 786 self.assertEqual(extract(), nolim[-2:]) 787 self.assertEqual(extract(limit=3), nolim[-3:]) 788 self.assertEqual(extract(limit=-3), nolim[:3]) 789 sys.tracebacklimit = 0 790 self.assertEqual(extract(), []) 791 sys.tracebacklimit = -1 792 self.assertEqual(extract(), []) 793 794 def test_extract_tb(self): 795 try: 796 self.last_raises5() 797 except Exception: 798 exc_type, exc_value, tb = sys.exc_info() 799 def extract(**kwargs): 800 return traceback.extract_tb(tb, **kwargs) 801 802 with support.swap_attr(sys, 'tracebacklimit', 1000): 803 nolim = extract() 804 self.assertEqual(len(nolim), 5+1) 805 self.assertEqual(extract(limit=2), nolim[:2]) 806 self.assertEqual(extract(limit=10), nolim) 807 self.assertEqual(extract(limit=-2), nolim[-2:]) 808 self.assertEqual(extract(limit=-10), nolim) 809 self.assertEqual(extract(limit=0), []) 810 del sys.tracebacklimit 811 self.assertEqual(extract(), nolim) 812 sys.tracebacklimit = 2 813 self.assertEqual(extract(), nolim[:2]) 814 self.assertEqual(extract(limit=3), nolim[:3]) 815 self.assertEqual(extract(limit=-3), nolim[-3:]) 816 sys.tracebacklimit = 0 817 self.assertEqual(extract(), []) 818 sys.tracebacklimit = -1 819 self.assertEqual(extract(), []) 820 821 def test_format_exception(self): 822 try: 823 self.last_raises5() 824 except Exception: 825 exc_type, exc_value, tb = sys.exc_info() 826 # [1:-1] to exclude "Traceback (...)" header and 827 # exception type and value 828 def extract(**kwargs): 829 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1] 830 831 with support.swap_attr(sys, 'tracebacklimit', 1000): 832 nolim = extract() 833 self.assertEqual(len(nolim), 5+1) 834 self.assertEqual(extract(limit=2), nolim[:2]) 835 self.assertEqual(extract(limit=10), nolim) 836 self.assertEqual(extract(limit=-2), nolim[-2:]) 837 self.assertEqual(extract(limit=-10), nolim) 838 self.assertEqual(extract(limit=0), []) 839 del sys.tracebacklimit 840 self.assertEqual(extract(), nolim) 841 sys.tracebacklimit = 2 842 self.assertEqual(extract(), nolim[:2]) 843 self.assertEqual(extract(limit=3), nolim[:3]) 844 self.assertEqual(extract(limit=-3), nolim[-3:]) 845 sys.tracebacklimit = 0 846 self.assertEqual(extract(), []) 847 sys.tracebacklimit = -1 848 self.assertEqual(extract(), []) 849 850 851class MiscTracebackCases(unittest.TestCase): 852 # 853 # Check non-printing functions in traceback module 854 # 855 856 def test_clear(self): 857 def outer(): 858 middle() 859 def middle(): 860 inner() 861 def inner(): 862 i = 1 863 1/0 864 865 try: 866 outer() 867 except: 868 type_, value, tb = sys.exc_info() 869 870 # Initial assertion: there's one local in the inner frame. 871 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame 872 self.assertEqual(len(inner_frame.f_locals), 1) 873 874 # Clear traceback frames 875 traceback.clear_frames(tb) 876 877 # Local variable dict should now be empty. 878 self.assertEqual(len(inner_frame.f_locals), 0) 879 880 def test_extract_stack(self): 881 def extract(): 882 return traceback.extract_stack() 883 result = extract() 884 lineno = extract.__code__.co_firstlineno 885 self.assertEqual(result[-2:], [ 886 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'), 887 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'), 888 ]) 889 self.assertEqual(len(result[0]), 4) 890 891 892class TestFrame(unittest.TestCase): 893 894 def test_basics(self): 895 linecache.clearcache() 896 linecache.lazycache("f", globals()) 897 f = traceback.FrameSummary("f", 1, "dummy") 898 self.assertEqual(f, 899 ("f", 1, "dummy", '"""Test cases for traceback module"""')) 900 self.assertEqual(tuple(f), 901 ("f", 1, "dummy", '"""Test cases for traceback module"""')) 902 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy")) 903 self.assertEqual(f, tuple(f)) 904 # Since tuple.__eq__ doesn't support FrameSummary, the equality 905 # operator fallbacks to FrameSummary.__eq__. 906 self.assertEqual(tuple(f), f) 907 self.assertIsNone(f.locals) 908 self.assertNotEqual(f, object()) 909 self.assertEqual(f, ALWAYS_EQ) 910 911 def test_lazy_lines(self): 912 linecache.clearcache() 913 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) 914 self.assertEqual(None, f._line) 915 linecache.lazycache("f", globals()) 916 self.assertEqual( 917 '"""Test cases for traceback module"""', 918 f.line) 919 920 def test_explicit_line(self): 921 f = traceback.FrameSummary("f", 1, "dummy", line="line") 922 self.assertEqual("line", f.line) 923 924 def test_len(self): 925 f = traceback.FrameSummary("f", 1, "dummy", line="line") 926 self.assertEqual(len(f), 4) 927 928 929class TestStack(unittest.TestCase): 930 931 def test_walk_stack(self): 932 def deeper(): 933 return list(traceback.walk_stack(None)) 934 s1 = list(traceback.walk_stack(None)) 935 s2 = deeper() 936 self.assertEqual(len(s2) - len(s1), 1) 937 self.assertEqual(s2[1:], s1) 938 939 def test_walk_tb(self): 940 try: 941 1/0 942 except Exception: 943 _, _, tb = sys.exc_info() 944 s = list(traceback.walk_tb(tb)) 945 self.assertEqual(len(s), 1) 946 947 def test_extract_stack(self): 948 s = traceback.StackSummary.extract(traceback.walk_stack(None)) 949 self.assertIsInstance(s, traceback.StackSummary) 950 951 def test_extract_stack_limit(self): 952 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5) 953 self.assertEqual(len(s), 5) 954 955 def test_extract_stack_lookup_lines(self): 956 linecache.clearcache() 957 linecache.updatecache('/foo.py', globals()) 958 c = test_code('/foo.py', 'method') 959 f = test_frame(c, None, None) 960 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True) 961 linecache.clearcache() 962 self.assertEqual(s[0].line, "import sys") 963 964 def test_extract_stackup_deferred_lookup_lines(self): 965 linecache.clearcache() 966 c = test_code('/foo.py', 'method') 967 f = test_frame(c, None, None) 968 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False) 969 self.assertEqual({}, linecache.cache) 970 linecache.updatecache('/foo.py', globals()) 971 self.assertEqual(s[0].line, "import sys") 972 973 def test_from_list(self): 974 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 975 self.assertEqual( 976 [' File "foo.py", line 1, in fred\n line\n'], 977 s.format()) 978 979 def test_from_list_edited_stack(self): 980 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 981 s[0] = ('foo.py', 2, 'fred', 'line') 982 s2 = traceback.StackSummary.from_list(s) 983 self.assertEqual( 984 [' File "foo.py", line 2, in fred\n line\n'], 985 s2.format()) 986 987 def test_format_smoke(self): 988 # For detailed tests see the format_list tests, which consume the same 989 # code. 990 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 991 self.assertEqual( 992 [' File "foo.py", line 1, in fred\n line\n'], 993 s.format()) 994 995 def test_locals(self): 996 linecache.updatecache('/foo.py', globals()) 997 c = test_code('/foo.py', 'method') 998 f = test_frame(c, globals(), {'something': 1}) 999 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True) 1000 self.assertEqual(s[0].locals, {'something': '1'}) 1001 1002 def test_no_locals(self): 1003 linecache.updatecache('/foo.py', globals()) 1004 c = test_code('/foo.py', 'method') 1005 f = test_frame(c, globals(), {'something': 1}) 1006 s = traceback.StackSummary.extract(iter([(f, 6)])) 1007 self.assertEqual(s[0].locals, None) 1008 1009 def test_format_locals(self): 1010 def some_inner(k, v): 1011 a = 1 1012 b = 2 1013 return traceback.StackSummary.extract( 1014 traceback.walk_stack(None), capture_locals=True, limit=1) 1015 s = some_inner(3, 4) 1016 self.assertEqual( 1017 [' File "%s", line %d, in some_inner\n' 1018 ' return traceback.StackSummary.extract(\n' 1019 ' a = 1\n' 1020 ' b = 2\n' 1021 ' k = 3\n' 1022 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3) 1023 ], s.format()) 1024 1025class TestTracebackException(unittest.TestCase): 1026 1027 def test_smoke(self): 1028 try: 1029 1/0 1030 except Exception: 1031 exc_info = sys.exc_info() 1032 exc = traceback.TracebackException(*exc_info) 1033 expected_stack = traceback.StackSummary.extract( 1034 traceback.walk_tb(exc_info[2])) 1035 self.assertEqual(None, exc.__cause__) 1036 self.assertEqual(None, exc.__context__) 1037 self.assertEqual(False, exc.__suppress_context__) 1038 self.assertEqual(expected_stack, exc.stack) 1039 self.assertEqual(exc_info[0], exc.exc_type) 1040 self.assertEqual(str(exc_info[1]), str(exc)) 1041 1042 def test_from_exception(self): 1043 # Check all the parameters are accepted. 1044 def foo(): 1045 1/0 1046 try: 1047 foo() 1048 except Exception as e: 1049 exc_info = sys.exc_info() 1050 self.expected_stack = traceback.StackSummary.extract( 1051 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, 1052 capture_locals=True) 1053 self.exc = traceback.TracebackException.from_exception( 1054 e, limit=1, lookup_lines=False, capture_locals=True) 1055 expected_stack = self.expected_stack 1056 exc = self.exc 1057 self.assertEqual(None, exc.__cause__) 1058 self.assertEqual(None, exc.__context__) 1059 self.assertEqual(False, exc.__suppress_context__) 1060 self.assertEqual(expected_stack, exc.stack) 1061 self.assertEqual(exc_info[0], exc.exc_type) 1062 self.assertEqual(str(exc_info[1]), str(exc)) 1063 1064 def test_cause(self): 1065 try: 1066 try: 1067 1/0 1068 finally: 1069 exc_info_context = sys.exc_info() 1070 exc_context = traceback.TracebackException(*exc_info_context) 1071 cause = Exception("cause") 1072 raise Exception("uh oh") from cause 1073 except Exception: 1074 exc_info = sys.exc_info() 1075 exc = traceback.TracebackException(*exc_info) 1076 expected_stack = traceback.StackSummary.extract( 1077 traceback.walk_tb(exc_info[2])) 1078 exc_cause = traceback.TracebackException(Exception, cause, None) 1079 self.assertEqual(exc_cause, exc.__cause__) 1080 self.assertEqual(exc_context, exc.__context__) 1081 self.assertEqual(True, exc.__suppress_context__) 1082 self.assertEqual(expected_stack, exc.stack) 1083 self.assertEqual(exc_info[0], exc.exc_type) 1084 self.assertEqual(str(exc_info[1]), str(exc)) 1085 1086 def test_context(self): 1087 try: 1088 try: 1089 1/0 1090 finally: 1091 exc_info_context = sys.exc_info() 1092 exc_context = traceback.TracebackException(*exc_info_context) 1093 raise Exception("uh oh") 1094 except Exception: 1095 exc_info = sys.exc_info() 1096 exc = traceback.TracebackException(*exc_info) 1097 expected_stack = traceback.StackSummary.extract( 1098 traceback.walk_tb(exc_info[2])) 1099 self.assertEqual(None, exc.__cause__) 1100 self.assertEqual(exc_context, exc.__context__) 1101 self.assertEqual(False, exc.__suppress_context__) 1102 self.assertEqual(expected_stack, exc.stack) 1103 self.assertEqual(exc_info[0], exc.exc_type) 1104 self.assertEqual(str(exc_info[1]), str(exc)) 1105 1106 def test_no_refs_to_exception_and_traceback_objects(self): 1107 try: 1108 1/0 1109 except Exception: 1110 exc_info = sys.exc_info() 1111 1112 refcnt1 = sys.getrefcount(exc_info[1]) 1113 refcnt2 = sys.getrefcount(exc_info[2]) 1114 exc = traceback.TracebackException(*exc_info) 1115 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1) 1116 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2) 1117 1118 def test_comparison_basic(self): 1119 try: 1120 1/0 1121 except Exception: 1122 exc_info = sys.exc_info() 1123 exc = traceback.TracebackException(*exc_info) 1124 exc2 = traceback.TracebackException(*exc_info) 1125 self.assertIsNot(exc, exc2) 1126 self.assertEqual(exc, exc2) 1127 self.assertNotEqual(exc, object()) 1128 self.assertEqual(exc, ALWAYS_EQ) 1129 1130 def test_comparison_params_variations(self): 1131 def raise_exc(): 1132 try: 1133 raise ValueError('bad value') 1134 except: 1135 raise 1136 1137 def raise_with_locals(): 1138 x, y = 1, 2 1139 raise_exc() 1140 1141 try: 1142 raise_with_locals() 1143 except Exception: 1144 exc_info = sys.exc_info() 1145 1146 exc = traceback.TracebackException(*exc_info) 1147 exc1 = traceback.TracebackException(*exc_info, limit=10) 1148 exc2 = traceback.TracebackException(*exc_info, limit=2) 1149 1150 self.assertEqual(exc, exc1) # limit=10 gets all frames 1151 self.assertNotEqual(exc, exc2) # limit=2 truncates the output 1152 1153 # locals change the output 1154 exc3 = traceback.TracebackException(*exc_info, capture_locals=True) 1155 self.assertNotEqual(exc, exc3) 1156 1157 # there are no locals in the innermost frame 1158 exc4 = traceback.TracebackException(*exc_info, limit=-1) 1159 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True) 1160 self.assertEqual(exc4, exc5) 1161 1162 # there are locals in the next-to-innermost frame 1163 exc6 = traceback.TracebackException(*exc_info, limit=-2) 1164 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True) 1165 self.assertNotEqual(exc6, exc7) 1166 1167 def test_comparison_equivalent_exceptions_are_equal(self): 1168 excs = [] 1169 for _ in range(2): 1170 try: 1171 1/0 1172 except: 1173 excs.append(traceback.TracebackException(*sys.exc_info())) 1174 self.assertEqual(excs[0], excs[1]) 1175 self.assertEqual(list(excs[0].format()), list(excs[1].format())) 1176 1177 def test_unhashable(self): 1178 class UnhashableException(Exception): 1179 def __eq__(self, other): 1180 return True 1181 1182 ex1 = UnhashableException('ex1') 1183 ex2 = UnhashableException('ex2') 1184 try: 1185 raise ex2 from ex1 1186 except UnhashableException: 1187 try: 1188 raise ex1 1189 except UnhashableException: 1190 exc_info = sys.exc_info() 1191 exc = traceback.TracebackException(*exc_info) 1192 formatted = list(exc.format()) 1193 self.assertIn('UnhashableException: ex2\n', formatted[2]) 1194 self.assertIn('UnhashableException: ex1\n', formatted[6]) 1195 1196 def test_limit(self): 1197 def recurse(n): 1198 if n: 1199 recurse(n-1) 1200 else: 1201 1/0 1202 try: 1203 recurse(10) 1204 except Exception: 1205 exc_info = sys.exc_info() 1206 exc = traceback.TracebackException(*exc_info, limit=5) 1207 expected_stack = traceback.StackSummary.extract( 1208 traceback.walk_tb(exc_info[2]), limit=5) 1209 self.assertEqual(expected_stack, exc.stack) 1210 1211 def test_lookup_lines(self): 1212 linecache.clearcache() 1213 e = Exception("uh oh") 1214 c = test_code('/foo.py', 'method') 1215 f = test_frame(c, None, None) 1216 tb = test_tb(f, 6, None) 1217 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) 1218 self.assertEqual(linecache.cache, {}) 1219 linecache.updatecache('/foo.py', globals()) 1220 self.assertEqual(exc.stack[0].line, "import sys") 1221 1222 def test_locals(self): 1223 linecache.updatecache('/foo.py', globals()) 1224 e = Exception("uh oh") 1225 c = test_code('/foo.py', 'method') 1226 f = test_frame(c, globals(), {'something': 1, 'other': 'string'}) 1227 tb = test_tb(f, 6, None) 1228 exc = traceback.TracebackException( 1229 Exception, e, tb, capture_locals=True) 1230 self.assertEqual( 1231 exc.stack[0].locals, {'something': '1', 'other': "'string'"}) 1232 1233 def test_no_locals(self): 1234 linecache.updatecache('/foo.py', globals()) 1235 e = Exception("uh oh") 1236 c = test_code('/foo.py', 'method') 1237 f = test_frame(c, globals(), {'something': 1}) 1238 tb = test_tb(f, 6, None) 1239 exc = traceback.TracebackException(Exception, e, tb) 1240 self.assertEqual(exc.stack[0].locals, None) 1241 1242 def test_traceback_header(self): 1243 # do not print a traceback header if exc_traceback is None 1244 # see issue #24695 1245 exc = traceback.TracebackException(Exception, Exception("haven"), None) 1246 self.assertEqual(list(exc.format()), ["Exception: haven\n"]) 1247 1248 1249class MiscTest(unittest.TestCase): 1250 1251 def test_all(self): 1252 expected = set() 1253 blacklist = {'print_list'} 1254 for name in dir(traceback): 1255 if name.startswith('_') or name in blacklist: 1256 continue 1257 module_object = getattr(traceback, name) 1258 if getattr(module_object, '__module__', None) == 'traceback': 1259 expected.add(name) 1260 self.assertCountEqual(traceback.__all__, expected) 1261 1262 1263if __name__ == "__main__": 1264 unittest.main() 1265