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