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