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, 12 requires_debug_ranges, has_no_debug_ranges) 13from test.support.os_helper import TESTFN, unlink 14from test.support.script_helper import assert_python_ok, assert_python_failure 15 16import os 17import textwrap 18import traceback 19from functools import partial 20 21 22test_code = namedtuple('code', ['co_filename', 'co_name']) 23test_code.co_positions = lambda _: iter([(6, 6, 0, 0)]) 24test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals']) 25test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next', 'tb_lasti']) 26 27 28class TracebackCases(unittest.TestCase): 29 # For now, a very minimal set of tests. I want to be sure that 30 # formatting of SyntaxErrors works based on changes for 2.1. 31 32 def get_exception_format(self, func, exc): 33 try: 34 func() 35 except exc as value: 36 return traceback.format_exception_only(exc, value) 37 else: 38 raise ValueError("call did not raise exception") 39 40 def syntax_error_with_caret(self): 41 compile("def fact(x):\n\treturn x!\n", "?", "exec") 42 43 def syntax_error_with_caret_2(self): 44 compile("1 +\n", "?", "exec") 45 46 def syntax_error_with_caret_range(self): 47 compile("f(x, y for y in range(30), z)", "?", "exec") 48 49 def syntax_error_bad_indentation(self): 50 compile("def spam():\n print(1)\n print(2)", "?", "exec") 51 52 def syntax_error_with_caret_non_ascii(self): 53 compile('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', "?", "exec") 54 55 def syntax_error_bad_indentation2(self): 56 compile(" print(2)", "?", "exec") 57 58 def tokenizer_error_with_caret_range(self): 59 compile("blech ( ", "?", "exec") 60 61 def test_caret(self): 62 err = self.get_exception_format(self.syntax_error_with_caret, 63 SyntaxError) 64 self.assertEqual(len(err), 4) 65 self.assertTrue(err[1].strip() == "return x!") 66 self.assertIn("^", err[2]) # third line has caret 67 self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place 68 self.assertEqual(err[2].count("^"), 1) 69 70 err = self.get_exception_format(self.syntax_error_with_caret_2, 71 SyntaxError) 72 self.assertIn("^", err[2]) # third line has caret 73 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 74 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place 75 self.assertEqual(err[2].count("^"), 1) 76 77 err = self.get_exception_format(self.syntax_error_with_caret_non_ascii, 78 SyntaxError) 79 self.assertIn("^", err[2]) # third line has caret 80 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 81 self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place 82 self.assertEqual(err[2].count("^"), 1) 83 84 err = self.get_exception_format(self.syntax_error_with_caret_range, 85 SyntaxError) 86 self.assertIn("^", err[2]) # third line has caret 87 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 88 self.assertEqual(err[1].find("y"), err[2].find("^")) # in the right place 89 self.assertEqual(err[2].count("^"), len("y for y in range(30)")) 90 91 err = self.get_exception_format(self.tokenizer_error_with_caret_range, 92 SyntaxError) 93 self.assertIn("^", err[2]) # third line has caret 94 self.assertEqual(err[2].count('\n'), 1) # and no additional newline 95 self.assertEqual(err[1].find("("), err[2].find("^")) # in the right place 96 self.assertEqual(err[2].count("^"), 1) 97 98 def test_nocaret(self): 99 exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) 100 err = traceback.format_exception_only(SyntaxError, exc) 101 self.assertEqual(len(err), 3) 102 self.assertEqual(err[1].strip(), "bad syntax") 103 104 def test_no_caret_with_no_debug_ranges_flag(self): 105 # Make sure that if `-X no_debug_ranges` is used, there are no carets 106 # in the traceback. 107 try: 108 with open(TESTFN, 'w') as f: 109 f.write("x = 1 / 0\n") 110 111 _, _, stderr = assert_python_failure( 112 '-X', 'no_debug_ranges', TESTFN) 113 114 lines = stderr.splitlines() 115 self.assertEqual(len(lines), 4) 116 self.assertEqual(lines[0], b'Traceback (most recent call last):') 117 self.assertIn(b'line 1, in <module>', lines[1]) 118 self.assertEqual(lines[2], b' x = 1 / 0') 119 self.assertEqual(lines[3], b'ZeroDivisionError: division by zero') 120 finally: 121 unlink(TESTFN) 122 123 def test_no_caret_with_no_debug_ranges_flag_python_traceback(self): 124 code = textwrap.dedent(""" 125 import traceback 126 try: 127 x = 1 / 0 128 except: 129 traceback.print_exc() 130 """) 131 try: 132 with open(TESTFN, 'w') as f: 133 f.write(code) 134 135 _, _, stderr = assert_python_ok( 136 '-X', 'no_debug_ranges', TESTFN) 137 138 lines = stderr.splitlines() 139 self.assertEqual(len(lines), 4) 140 self.assertEqual(lines[0], b'Traceback (most recent call last):') 141 self.assertIn(b'line 4, in <module>', lines[1]) 142 self.assertEqual(lines[2], b' x = 1 / 0') 143 self.assertEqual(lines[3], b'ZeroDivisionError: division by zero') 144 finally: 145 unlink(TESTFN) 146 147 def test_recursion_error_during_traceback(self): 148 code = textwrap.dedent(""" 149 import sys 150 from weakref import ref 151 152 sys.setrecursionlimit(15) 153 154 def f(): 155 ref(lambda: 0, []) 156 f() 157 158 try: 159 f() 160 except RecursionError: 161 pass 162 """) 163 try: 164 with open(TESTFN, 'w') as f: 165 f.write(code) 166 167 rc, _, _ = assert_python_ok(TESTFN) 168 self.assertEqual(rc, 0) 169 finally: 170 unlink(TESTFN) 171 172 def test_bad_indentation(self): 173 err = self.get_exception_format(self.syntax_error_bad_indentation, 174 IndentationError) 175 self.assertEqual(len(err), 4) 176 self.assertEqual(err[1].strip(), "print(2)") 177 self.assertIn("^", err[2]) 178 self.assertEqual(err[1].find(")") + 1, err[2].find("^")) 179 180 # No caret for "unexpected indent" 181 err = self.get_exception_format(self.syntax_error_bad_indentation2, 182 IndentationError) 183 self.assertEqual(len(err), 3) 184 self.assertEqual(err[1].strip(), "print(2)") 185 186 def test_base_exception(self): 187 # Test that exceptions derived from BaseException are formatted right 188 e = KeyboardInterrupt() 189 lst = traceback.format_exception_only(e.__class__, e) 190 self.assertEqual(lst, ['KeyboardInterrupt\n']) 191 192 def test_format_exception_only_bad__str__(self): 193 class X(Exception): 194 def __str__(self): 195 1/0 196 err = traceback.format_exception_only(X, X()) 197 self.assertEqual(len(err), 1) 198 str_value = '<exception str() failed>' 199 if X.__module__ in ('__main__', 'builtins'): 200 str_name = X.__qualname__ 201 else: 202 str_name = '.'.join([X.__module__, X.__qualname__]) 203 self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) 204 205 def test_encoded_file(self): 206 # Test that tracebacks are correctly printed for encoded source files: 207 # - correct line number (Issue2384) 208 # - respect file encoding (Issue3975) 209 import sys, subprocess 210 211 # The spawned subprocess has its stdout redirected to a PIPE, and its 212 # encoding may be different from the current interpreter, on Windows 213 # at least. 214 process = subprocess.Popen([sys.executable, "-c", 215 "import sys; print(sys.stdout.encoding)"], 216 stdout=subprocess.PIPE, 217 stderr=subprocess.STDOUT) 218 stdout, stderr = process.communicate() 219 output_encoding = str(stdout, 'ascii').splitlines()[0] 220 221 def do_test(firstlines, message, charset, lineno): 222 # Raise the message in a subprocess, and catch the output 223 try: 224 with open(TESTFN, "w", encoding=charset) as output: 225 output.write("""{0}if 1: 226 import traceback; 227 raise RuntimeError('{1}') 228 """.format(firstlines, message)) 229 230 process = subprocess.Popen([sys.executable, TESTFN], 231 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 232 stdout, stderr = process.communicate() 233 stdout = stdout.decode(output_encoding).splitlines() 234 finally: 235 unlink(TESTFN) 236 237 # The source lines are encoded with the 'backslashreplace' handler 238 encoded_message = message.encode(output_encoding, 239 'backslashreplace') 240 # and we just decoded them with the output_encoding. 241 message_ascii = encoded_message.decode(output_encoding) 242 243 err_line = "raise RuntimeError('{0}')".format(message_ascii) 244 err_msg = "RuntimeError: {0}".format(message_ascii) 245 246 self.assertIn(("line %s" % lineno), stdout[1], 247 "Invalid line number: {0!r} instead of {1}".format( 248 stdout[1], lineno)) 249 self.assertTrue(stdout[2].endswith(err_line), 250 "Invalid traceback line: {0!r} instead of {1!r}".format( 251 stdout[2], err_line)) 252 actual_err_msg = stdout[3 if has_no_debug_ranges() else 4] 253 self.assertTrue(actual_err_msg == err_msg, 254 "Invalid error message: {0!r} instead of {1!r}".format( 255 actual_err_msg, err_msg)) 256 257 do_test("", "foo", "ascii", 3) 258 for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"): 259 if charset == "ascii": 260 text = "foo" 261 elif charset == "GBK": 262 text = "\u4E02\u5100" 263 else: 264 text = "h\xe9 ho" 265 do_test("# coding: {0}\n".format(charset), 266 text, charset, 4) 267 do_test("#!shebang\n# coding: {0}\n".format(charset), 268 text, charset, 5) 269 do_test(" \t\f\n# coding: {0}\n".format(charset), 270 text, charset, 5) 271 # Issue #18960: coding spec should have no effect 272 do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) 273 274 def test_print_traceback_at_exit(self): 275 # Issue #22599: Ensure that it is possible to use the traceback module 276 # to display an exception at Python exit 277 code = textwrap.dedent(""" 278 import sys 279 import traceback 280 281 class PrintExceptionAtExit(object): 282 def __init__(self): 283 try: 284 x = 1 / 0 285 except Exception: 286 self.exc_info = sys.exc_info() 287 # self.exc_info[1] (traceback) contains frames: 288 # explicitly clear the reference to self in the current 289 # frame to break a reference cycle 290 self = None 291 292 def __del__(self): 293 traceback.print_exception(*self.exc_info) 294 295 # Keep a reference in the module namespace to call the destructor 296 # when the module is unloaded 297 obj = PrintExceptionAtExit() 298 """) 299 rc, stdout, stderr = assert_python_ok('-c', code) 300 expected = [b'Traceback (most recent call last):', 301 b' File "<string>", line 8, in __init__', 302 b'ZeroDivisionError: division by zero'] 303 self.assertEqual(stderr.splitlines(), expected) 304 305 def test_print_exception(self): 306 output = StringIO() 307 traceback.print_exception( 308 Exception, Exception("projector"), None, file=output 309 ) 310 self.assertEqual(output.getvalue(), "Exception: projector\n") 311 312 def test_print_exception_exc(self): 313 output = StringIO() 314 traceback.print_exception(Exception("projector"), file=output) 315 self.assertEqual(output.getvalue(), "Exception: projector\n") 316 317 def test_format_exception_exc(self): 318 e = Exception("projector") 319 output = traceback.format_exception(e) 320 self.assertEqual(output, ["Exception: projector\n"]) 321 with self.assertRaisesRegex(ValueError, 'Both or neither'): 322 traceback.format_exception(e.__class__, e) 323 with self.assertRaisesRegex(ValueError, 'Both or neither'): 324 traceback.format_exception(e.__class__, tb=e.__traceback__) 325 with self.assertRaisesRegex(TypeError, 'positional-only'): 326 traceback.format_exception(exc=e) 327 328 def test_format_exception_only_exc(self): 329 output = traceback.format_exception_only(Exception("projector")) 330 self.assertEqual(output, ["Exception: projector\n"]) 331 332 def test_exception_is_None(self): 333 NONE_EXC_STRING = 'NoneType: None\n' 334 excfile = StringIO() 335 traceback.print_exception(None, file=excfile) 336 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING) 337 338 excfile = StringIO() 339 traceback.print_exception(None, None, None, file=excfile) 340 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING) 341 342 excfile = StringIO() 343 traceback.print_exc(None, file=excfile) 344 self.assertEqual(excfile.getvalue(), NONE_EXC_STRING) 345 346 self.assertEqual(traceback.format_exc(None), NONE_EXC_STRING) 347 self.assertEqual(traceback.format_exception(None), [NONE_EXC_STRING]) 348 self.assertEqual( 349 traceback.format_exception(None, None, None), [NONE_EXC_STRING]) 350 self.assertEqual( 351 traceback.format_exception_only(None), [NONE_EXC_STRING]) 352 self.assertEqual( 353 traceback.format_exception_only(None, None), [NONE_EXC_STRING]) 354 355 def test_signatures(self): 356 self.assertEqual( 357 str(inspect.signature(traceback.print_exception)), 358 ('(exc, /, value=<implicit>, tb=<implicit>, ' 359 'limit=None, file=None, chain=True)')) 360 361 self.assertEqual( 362 str(inspect.signature(traceback.format_exception)), 363 ('(exc, /, value=<implicit>, tb=<implicit>, limit=None, ' 364 'chain=True)')) 365 366 self.assertEqual( 367 str(inspect.signature(traceback.format_exception_only)), 368 '(exc, /, value=<implicit>)') 369 370 371@requires_debug_ranges() 372class TracebackErrorLocationCaretTests(unittest.TestCase): 373 """ 374 Tests for printing code error expressions as part of PEP 657 375 """ 376 def get_exception(self, callable): 377 try: 378 callable() 379 self.fail("No exception thrown.") 380 except: 381 return traceback.format_exc().splitlines()[:-1] 382 383 callable_line = get_exception.__code__.co_firstlineno + 2 384 385 def test_basic_caret(self): 386 def f(): 387 raise ValueError("basic caret tests") 388 389 lineno_f = f.__code__.co_firstlineno 390 expected_f = ( 391 'Traceback (most recent call last):\n' 392 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 393 ' callable()\n' 394 ' ^^^^^^^^^^\n' 395 f' File "{__file__}", line {lineno_f+1}, in f\n' 396 ' raise ValueError("basic caret tests")\n' 397 ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 398 ) 399 result_lines = self.get_exception(f) 400 self.assertEqual(result_lines, expected_f.splitlines()) 401 402 def test_line_with_unicode(self): 403 # Make sure that even if a line contains multi-byte unicode characters 404 # the correct carets are printed. 405 def f_with_unicode(): 406 raise ValueError("Ĥellö Wörld") 407 408 lineno_f = f_with_unicode.__code__.co_firstlineno 409 expected_f = ( 410 'Traceback (most recent call last):\n' 411 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 412 ' callable()\n' 413 ' ^^^^^^^^^^\n' 414 f' File "{__file__}", line {lineno_f+1}, in f_with_unicode\n' 415 ' raise ValueError("Ĥellö Wörld")\n' 416 ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 417 ) 418 result_lines = self.get_exception(f_with_unicode) 419 self.assertEqual(result_lines, expected_f.splitlines()) 420 421 def test_caret_in_type_annotation(self): 422 def f_with_type(): 423 def foo(a: THIS_DOES_NOT_EXIST ) -> int: 424 return 0 425 426 lineno_f = f_with_type.__code__.co_firstlineno 427 expected_f = ( 428 'Traceback (most recent call last):\n' 429 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 430 ' callable()\n' 431 ' ^^^^^^^^^^\n' 432 f' File "{__file__}", line {lineno_f+1}, in f_with_type\n' 433 ' def foo(a: THIS_DOES_NOT_EXIST ) -> int:\n' 434 ' ^^^^^^^^^^^^^^^^^^^\n' 435 ) 436 result_lines = self.get_exception(f_with_type) 437 self.assertEqual(result_lines, expected_f.splitlines()) 438 439 def test_caret_multiline_expression(self): 440 # Make sure no carets are printed for expressions spanning multiple 441 # lines. 442 def f_with_multiline(): 443 raise ValueError( 444 "error over multiple lines" 445 ) 446 447 lineno_f = f_with_multiline.__code__.co_firstlineno 448 expected_f = ( 449 'Traceback (most recent call last):\n' 450 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 451 ' callable()\n' 452 ' ^^^^^^^^^^\n' 453 f' File "{__file__}", line {lineno_f+1}, in f_with_multiline\n' 454 ' raise ValueError(\n' 455 ' ^^^^^^^^^^^^^^^^^' 456 ) 457 result_lines = self.get_exception(f_with_multiline) 458 self.assertEqual(result_lines, expected_f.splitlines()) 459 460 def test_caret_multiline_expression_bin_op(self): 461 # Make sure no carets are printed for expressions spanning multiple 462 # lines. 463 def f_with_multiline(): 464 return ( 465 1 / 466 0 + 467 2 468 ) 469 470 lineno_f = f_with_multiline.__code__.co_firstlineno 471 expected_f = ( 472 'Traceback (most recent call last):\n' 473 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 474 ' callable()\n' 475 ' ^^^^^^^^^^\n' 476 f' File "{__file__}", line {lineno_f+2}, in f_with_multiline\n' 477 ' 1 /\n' 478 ' ^^^' 479 ) 480 result_lines = self.get_exception(f_with_multiline) 481 self.assertEqual(result_lines, expected_f.splitlines()) 482 483 def test_caret_for_binary_operators(self): 484 def f_with_binary_operator(): 485 divisor = 20 486 return 10 + divisor / 0 + 30 487 488 lineno_f = f_with_binary_operator.__code__.co_firstlineno 489 expected_error = ( 490 'Traceback (most recent call last):\n' 491 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 492 ' callable()\n' 493 ' ^^^^^^^^^^\n' 494 f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' 495 ' return 10 + divisor / 0 + 30\n' 496 ' ~~~~~~~~^~~\n' 497 ) 498 result_lines = self.get_exception(f_with_binary_operator) 499 self.assertEqual(result_lines, expected_error.splitlines()) 500 501 def test_caret_for_binary_operators_two_char(self): 502 def f_with_binary_operator(): 503 divisor = 20 504 return 10 + divisor // 0 + 30 505 506 lineno_f = f_with_binary_operator.__code__.co_firstlineno 507 expected_error = ( 508 'Traceback (most recent call last):\n' 509 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 510 ' callable()\n' 511 ' ^^^^^^^^^^\n' 512 f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' 513 ' return 10 + divisor // 0 + 30\n' 514 ' ~~~~~~~~^^~~\n' 515 ) 516 result_lines = self.get_exception(f_with_binary_operator) 517 self.assertEqual(result_lines, expected_error.splitlines()) 518 519 def test_caret_for_subscript(self): 520 def f_with_subscript(): 521 some_dict = {'x': {'y': None}} 522 return some_dict['x']['y']['z'] 523 524 lineno_f = f_with_subscript.__code__.co_firstlineno 525 expected_error = ( 526 'Traceback (most recent call last):\n' 527 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 528 ' callable()\n' 529 ' ^^^^^^^^^^\n' 530 f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' 531 " return some_dict['x']['y']['z']\n" 532 ' ~~~~~~~~~~~~~~~~~~~^^^^^\n' 533 ) 534 result_lines = self.get_exception(f_with_subscript) 535 self.assertEqual(result_lines, expected_error.splitlines()) 536 537 def test_traceback_specialization_with_syntax_error(self): 538 bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec") 539 540 with open(TESTFN, "w") as file: 541 # make the file's contents invalid 542 file.write("1 $ 0 / 1 / 2\n") 543 self.addCleanup(unlink, TESTFN) 544 545 func = partial(exec, bytecode) 546 result_lines = self.get_exception(func) 547 548 lineno_f = bytecode.co_firstlineno 549 expected_error = ( 550 'Traceback (most recent call last):\n' 551 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 552 ' callable()\n' 553 ' ^^^^^^^^^^\n' 554 f' File "{TESTFN}", line {lineno_f}, in <module>\n' 555 " 1 $ 0 / 1 / 2\n" 556 ' ^^^^^\n' 557 ) 558 self.assertEqual(result_lines, expected_error.splitlines()) 559 560 def test_traceback_very_long_line(self): 561 source = "a" * 256 562 bytecode = compile(source, TESTFN, "exec") 563 564 with open(TESTFN, "w") as file: 565 file.write(source) 566 self.addCleanup(unlink, TESTFN) 567 568 func = partial(exec, bytecode) 569 result_lines = self.get_exception(func) 570 571 lineno_f = bytecode.co_firstlineno 572 expected_error = ( 573 'Traceback (most recent call last):\n' 574 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 575 ' callable()\n' 576 ' ^^^^^^^^^^\n' 577 f' File "{TESTFN}", line {lineno_f}, in <module>\n' 578 f' {source}\n' 579 ) 580 self.assertEqual(result_lines, expected_error.splitlines()) 581 582 def assertSpecialized(self, func, expected_specialization): 583 result_lines = self.get_exception(func) 584 specialization_line = result_lines[-1] 585 self.assertEqual(specialization_line.lstrip(), expected_specialization) 586 587 def test_specialization_variations(self): 588 self.assertSpecialized(lambda: 1/0, 589 "~^~") 590 self.assertSpecialized(lambda: 1/0/3, 591 "~^~") 592 self.assertSpecialized(lambda: 1 / 0, 593 "~~^~~") 594 self.assertSpecialized(lambda: 1 / 0 / 3, 595 "~~^~~") 596 self.assertSpecialized(lambda: 1/ 0, 597 "~^~~") 598 self.assertSpecialized(lambda: 1/ 0/3, 599 "~^~~") 600 self.assertSpecialized(lambda: 1 / 0, 601 "~~~~~^~~~") 602 self.assertSpecialized(lambda: 1 / 0 / 5, 603 "~~~~~^~~~") 604 self.assertSpecialized(lambda: 1 /0, 605 "~~^~") 606 self.assertSpecialized(lambda: 1//0, 607 "~^^~") 608 self.assertSpecialized(lambda: 1//0//4, 609 "~^^~") 610 self.assertSpecialized(lambda: 1 // 0, 611 "~~^^~~") 612 self.assertSpecialized(lambda: 1 // 0 // 4, 613 "~~^^~~") 614 self.assertSpecialized(lambda: 1 //0, 615 "~~^^~") 616 self.assertSpecialized(lambda: 1// 0, 617 "~^^~~") 618 619 620@cpython_only 621@requires_debug_ranges() 622class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests): 623 """ 624 Same set of tests as above but with Python's internal traceback printing. 625 """ 626 def get_exception(self, callable): 627 from _testcapi import traceback_print 628 try: 629 callable() 630 self.fail("No exception thrown.") 631 except: 632 type_, value, tb = sys.exc_info() 633 634 file_ = StringIO() 635 traceback_print(tb, file_) 636 return file_.getvalue().splitlines() 637 638 callable_line = get_exception.__code__.co_firstlineno + 3 639 640 641class TracebackFormatTests(unittest.TestCase): 642 643 def some_exception(self): 644 raise KeyError('blah') 645 646 @cpython_only 647 def check_traceback_format(self, cleanup_func=None): 648 from _testcapi import traceback_print 649 try: 650 self.some_exception() 651 except KeyError: 652 type_, value, tb = sys.exc_info() 653 if cleanup_func is not None: 654 # Clear the inner frames, not this one 655 cleanup_func(tb.tb_next) 656 traceback_fmt = 'Traceback (most recent call last):\n' + \ 657 ''.join(traceback.format_tb(tb)) 658 file_ = StringIO() 659 traceback_print(tb, file_) 660 python_fmt = file_.getvalue() 661 # Call all _tb and _exc functions 662 with captured_output("stderr") as tbstderr: 663 traceback.print_tb(tb) 664 tbfile = StringIO() 665 traceback.print_tb(tb, file=tbfile) 666 with captured_output("stderr") as excstderr: 667 traceback.print_exc() 668 excfmt = traceback.format_exc() 669 excfile = StringIO() 670 traceback.print_exc(file=excfile) 671 else: 672 raise Error("unable to create test traceback string") 673 674 # Make sure that Python and the traceback module format the same thing 675 self.assertEqual(traceback_fmt, python_fmt) 676 # Now verify the _tb func output 677 self.assertEqual(tbstderr.getvalue(), tbfile.getvalue()) 678 # Now verify the _exc func output 679 self.assertEqual(excstderr.getvalue(), excfile.getvalue()) 680 self.assertEqual(excfmt, excfile.getvalue()) 681 682 # Make sure that the traceback is properly indented. 683 tb_lines = python_fmt.splitlines() 684 banner = tb_lines[0] 685 if has_no_debug_ranges(): 686 self.assertEqual(len(tb_lines), 5) 687 location, source_line = tb_lines[-2], tb_lines[-1] 688 else: 689 self.assertEqual(len(tb_lines), 7) 690 location, source_line = tb_lines[-3], tb_lines[-2] 691 self.assertTrue(banner.startswith('Traceback')) 692 self.assertTrue(location.startswith(' File')) 693 self.assertTrue(source_line.startswith(' raise')) 694 695 def test_traceback_format(self): 696 self.check_traceback_format() 697 698 def test_traceback_format_with_cleared_frames(self): 699 # Check that traceback formatting also works with a clear()ed frame 700 def cleanup_tb(tb): 701 tb.tb_frame.clear() 702 self.check_traceback_format(cleanup_tb) 703 704 def test_stack_format(self): 705 # Verify _stack functions. Note we have to use _getframe(1) to 706 # compare them without this frame appearing in the output 707 with captured_output("stderr") as ststderr: 708 traceback.print_stack(sys._getframe(1)) 709 stfile = StringIO() 710 traceback.print_stack(sys._getframe(1), file=stfile) 711 self.assertEqual(ststderr.getvalue(), stfile.getvalue()) 712 713 stfmt = traceback.format_stack(sys._getframe(1)) 714 715 self.assertEqual(ststderr.getvalue(), "".join(stfmt)) 716 717 def test_print_stack(self): 718 def prn(): 719 traceback.print_stack() 720 with captured_output("stderr") as stderr: 721 prn() 722 lineno = prn.__code__.co_firstlineno 723 self.assertEqual(stderr.getvalue().splitlines()[-4:], [ 724 ' File "%s", line %d, in test_print_stack' % (__file__, lineno+3), 725 ' prn()', 726 ' File "%s", line %d, in prn' % (__file__, lineno+1), 727 ' traceback.print_stack()', 728 ]) 729 730 # issue 26823 - Shrink recursive tracebacks 731 def _check_recursive_traceback_display(self, render_exc): 732 # Always show full diffs when this test fails 733 # Note that rearranging things may require adjusting 734 # the relative line numbers in the expected tracebacks 735 self.maxDiff = None 736 737 # Check hitting the recursion limit 738 def f(): 739 f() 740 741 with captured_output("stderr") as stderr_f: 742 try: 743 f() 744 except RecursionError: 745 render_exc() 746 else: 747 self.fail("no recursion occurred") 748 749 lineno_f = f.__code__.co_firstlineno 750 result_f = ( 751 'Traceback (most recent call last):\n' 752 f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' 753 ' f()\n' 754 ' ^^^\n' 755 f' File "{__file__}", line {lineno_f+1}, in f\n' 756 ' f()\n' 757 ' ^^^\n' 758 f' File "{__file__}", line {lineno_f+1}, in f\n' 759 ' f()\n' 760 ' ^^^\n' 761 f' File "{__file__}", line {lineno_f+1}, in f\n' 762 ' f()\n' 763 ' ^^^\n' 764 # XXX: The following line changes depending on whether the tests 765 # are run through the interactive interpreter or with -m 766 # It also varies depending on the platform (stack size) 767 # Fortunately, we don't care about exactness here, so we use regex 768 r' \[Previous line repeated (\d+) more times\]' '\n' 769 'RecursionError: maximum recursion depth exceeded\n' 770 ) 771 772 expected = result_f.splitlines() 773 actual = stderr_f.getvalue().splitlines() 774 775 # Check the output text matches expectations 776 # 2nd last line contains the repetition count 777 self.assertEqual(actual[:-2], expected[:-2]) 778 self.assertRegex(actual[-2], expected[-2]) 779 # last line can have additional text appended 780 self.assertIn(expected[-1], actual[-1]) 781 782 # Check the recursion count is roughly as expected 783 rec_limit = sys.getrecursionlimit() 784 self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit)) 785 786 # Check a known (limited) number of recursive invocations 787 def g(count=10): 788 if count: 789 return g(count-1) 790 raise ValueError 791 792 with captured_output("stderr") as stderr_g: 793 try: 794 g() 795 except ValueError: 796 render_exc() 797 else: 798 self.fail("no value error was raised") 799 800 lineno_g = g.__code__.co_firstlineno 801 result_g = ( 802 f' File "{__file__}", line {lineno_g+2}, in g\n' 803 ' return g(count-1)\n' 804 ' ^^^^^^^^^^\n' 805 f' File "{__file__}", line {lineno_g+2}, in g\n' 806 ' return g(count-1)\n' 807 ' ^^^^^^^^^^\n' 808 f' File "{__file__}", line {lineno_g+2}, in g\n' 809 ' return g(count-1)\n' 810 ' ^^^^^^^^^^\n' 811 ' [Previous line repeated 7 more times]\n' 812 f' File "{__file__}", line {lineno_g+3}, in g\n' 813 ' raise ValueError\n' 814 ' ^^^^^^^^^^^^^^^^\n' 815 'ValueError\n' 816 ) 817 tb_line = ( 818 'Traceback (most recent call last):\n' 819 f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' 820 ' g()\n' 821 ' ^^^\n' 822 ) 823 expected = (tb_line + result_g).splitlines() 824 actual = stderr_g.getvalue().splitlines() 825 self.assertEqual(actual, expected) 826 827 # Check 2 different repetitive sections 828 def h(count=10): 829 if count: 830 return h(count-1) 831 g() 832 833 with captured_output("stderr") as stderr_h: 834 try: 835 h() 836 except ValueError: 837 render_exc() 838 else: 839 self.fail("no value error was raised") 840 841 lineno_h = h.__code__.co_firstlineno 842 result_h = ( 843 'Traceback (most recent call last):\n' 844 f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' 845 ' h()\n' 846 ' ^^^\n' 847 f' File "{__file__}", line {lineno_h+2}, in h\n' 848 ' return h(count-1)\n' 849 ' ^^^^^^^^^^\n' 850 f' File "{__file__}", line {lineno_h+2}, in h\n' 851 ' return h(count-1)\n' 852 ' ^^^^^^^^^^\n' 853 f' File "{__file__}", line {lineno_h+2}, in h\n' 854 ' return h(count-1)\n' 855 ' ^^^^^^^^^^\n' 856 ' [Previous line repeated 7 more times]\n' 857 f' File "{__file__}", line {lineno_h+3}, in h\n' 858 ' g()\n' 859 ' ^^^\n' 860 ) 861 expected = (result_h + result_g).splitlines() 862 actual = stderr_h.getvalue().splitlines() 863 self.assertEqual(actual, expected) 864 865 # Check the boundary conditions. First, test just below the cutoff. 866 with captured_output("stderr") as stderr_g: 867 try: 868 g(traceback._RECURSIVE_CUTOFF) 869 except ValueError: 870 render_exc() 871 else: 872 self.fail("no error raised") 873 result_g = ( 874 f' File "{__file__}", line {lineno_g+2}, in g\n' 875 ' return g(count-1)\n' 876 ' ^^^^^^^^^^\n' 877 f' File "{__file__}", line {lineno_g+2}, in g\n' 878 ' return g(count-1)\n' 879 ' ^^^^^^^^^^\n' 880 f' File "{__file__}", line {lineno_g+2}, in g\n' 881 ' return g(count-1)\n' 882 ' ^^^^^^^^^^\n' 883 f' File "{__file__}", line {lineno_g+3}, in g\n' 884 ' raise ValueError\n' 885 ' ^^^^^^^^^^^^^^^^\n' 886 'ValueError\n' 887 ) 888 tb_line = ( 889 'Traceback (most recent call last):\n' 890 f' File "{__file__}", line {lineno_g+81}, in _check_recursive_traceback_display\n' 891 ' g(traceback._RECURSIVE_CUTOFF)\n' 892 ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 893 ) 894 expected = (tb_line + result_g).splitlines() 895 actual = stderr_g.getvalue().splitlines() 896 self.assertEqual(actual, expected) 897 898 # Second, test just above the cutoff. 899 with captured_output("stderr") as stderr_g: 900 try: 901 g(traceback._RECURSIVE_CUTOFF + 1) 902 except ValueError: 903 render_exc() 904 else: 905 self.fail("no error raised") 906 result_g = ( 907 f' File "{__file__}", line {lineno_g+2}, in g\n' 908 ' return g(count-1)\n' 909 ' ^^^^^^^^^^\n' 910 f' File "{__file__}", line {lineno_g+2}, in g\n' 911 ' return g(count-1)\n' 912 ' ^^^^^^^^^^\n' 913 f' File "{__file__}", line {lineno_g+2}, in g\n' 914 ' return g(count-1)\n' 915 ' ^^^^^^^^^^\n' 916 ' [Previous line repeated 1 more time]\n' 917 f' File "{__file__}", line {lineno_g+3}, in g\n' 918 ' raise ValueError\n' 919 ' ^^^^^^^^^^^^^^^^\n' 920 'ValueError\n' 921 ) 922 tb_line = ( 923 'Traceback (most recent call last):\n' 924 f' File "{__file__}", line {lineno_g+114}, in _check_recursive_traceback_display\n' 925 ' g(traceback._RECURSIVE_CUTOFF + 1)\n' 926 ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 927 ) 928 expected = (tb_line + result_g).splitlines() 929 actual = stderr_g.getvalue().splitlines() 930 self.assertEqual(actual, expected) 931 932 @requires_debug_ranges() 933 def test_recursive_traceback_python(self): 934 self._check_recursive_traceback_display(traceback.print_exc) 935 936 @cpython_only 937 @requires_debug_ranges() 938 def test_recursive_traceback_cpython_internal(self): 939 from _testcapi import exception_print 940 def render_exc(): 941 exc_type, exc_value, exc_tb = sys.exc_info() 942 exception_print(exc_value) 943 self._check_recursive_traceback_display(render_exc) 944 945 def test_format_stack(self): 946 def fmt(): 947 return traceback.format_stack() 948 result = fmt() 949 lineno = fmt.__code__.co_firstlineno 950 self.assertEqual(result[-2:], [ 951 ' File "%s", line %d, in test_format_stack\n' 952 ' result = fmt()\n' % (__file__, lineno+2), 953 ' File "%s", line %d, in fmt\n' 954 ' return traceback.format_stack()\n' % (__file__, lineno+1), 955 ]) 956 957 @cpython_only 958 def test_unhashable(self): 959 from _testcapi import exception_print 960 961 class UnhashableException(Exception): 962 def __eq__(self, other): 963 return True 964 965 ex1 = UnhashableException('ex1') 966 ex2 = UnhashableException('ex2') 967 try: 968 raise ex2 from ex1 969 except UnhashableException: 970 try: 971 raise ex1 972 except UnhashableException: 973 exc_type, exc_val, exc_tb = sys.exc_info() 974 975 with captured_output("stderr") as stderr_f: 976 exception_print(exc_val) 977 978 tb = stderr_f.getvalue().strip().splitlines() 979 if has_no_debug_ranges(): 980 self.assertEqual(11, len(tb)) 981 self.assertEqual(context_message.strip(), tb[5]) 982 self.assertIn('UnhashableException: ex2', tb[3]) 983 self.assertIn('UnhashableException: ex1', tb[10]) 984 else: 985 self.assertEqual(13, len(tb)) 986 self.assertEqual(context_message.strip(), tb[6]) 987 self.assertIn('UnhashableException: ex2', tb[4]) 988 self.assertIn('UnhashableException: ex1', tb[12]) 989 990 def deep_eg(self): 991 e = TypeError(1) 992 for i in range(2000): 993 e = ExceptionGroup('eg', [e]) 994 return e 995 996 @cpython_only 997 def test_exception_group_deep_recursion_capi(self): 998 from _testcapi import exception_print 999 LIMIT = 75 1000 eg = self.deep_eg() 1001 with captured_output("stderr") as stderr_f: 1002 with support.infinite_recursion(max_depth=LIMIT): 1003 exception_print(eg) 1004 output = stderr_f.getvalue() 1005 self.assertIn('ExceptionGroup', output) 1006 self.assertLessEqual(output.count('ExceptionGroup'), LIMIT) 1007 1008 def test_exception_group_deep_recursion_traceback(self): 1009 LIMIT = 75 1010 eg = self.deep_eg() 1011 with captured_output("stderr") as stderr_f: 1012 with support.infinite_recursion(max_depth=LIMIT): 1013 traceback.print_exception(type(eg), eg, eg.__traceback__) 1014 output = stderr_f.getvalue() 1015 self.assertIn('ExceptionGroup', output) 1016 self.assertLessEqual(output.count('ExceptionGroup'), LIMIT) 1017 1018 1019cause_message = ( 1020 "\nThe above exception was the direct cause " 1021 "of the following exception:\n\n") 1022 1023context_message = ( 1024 "\nDuring handling of the above exception, " 1025 "another exception occurred:\n\n") 1026 1027boundaries = re.compile( 1028 '(%s|%s)' % (re.escape(cause_message), re.escape(context_message))) 1029 1030class BaseExceptionReportingTests: 1031 1032 def get_exception(self, exception_or_callable): 1033 if isinstance(exception_or_callable, Exception): 1034 return exception_or_callable 1035 try: 1036 exception_or_callable() 1037 except Exception as e: 1038 return e 1039 1040 callable_line = get_exception.__code__.co_firstlineno + 4 1041 1042 def zero_div(self): 1043 1/0 # In zero_div 1044 1045 def check_zero_div(self, msg): 1046 lines = msg.splitlines() 1047 if has_no_debug_ranges(): 1048 self.assertTrue(lines[-3].startswith(' File')) 1049 self.assertIn('1/0 # In zero_div', lines[-2]) 1050 else: 1051 self.assertTrue(lines[-4].startswith(' File')) 1052 self.assertIn('1/0 # In zero_div', lines[-3]) 1053 self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1]) 1054 1055 def test_simple(self): 1056 try: 1057 1/0 # Marker 1058 except ZeroDivisionError as _: 1059 e = _ 1060 lines = self.get_report(e).splitlines() 1061 if has_no_debug_ranges(): 1062 self.assertEqual(len(lines), 4) 1063 self.assertTrue(lines[3].startswith('ZeroDivisionError')) 1064 else: 1065 self.assertEqual(len(lines), 5) 1066 self.assertTrue(lines[4].startswith('ZeroDivisionError')) 1067 self.assertTrue(lines[0].startswith('Traceback')) 1068 self.assertTrue(lines[1].startswith(' File')) 1069 self.assertIn('1/0 # Marker', lines[2]) 1070 1071 def test_cause(self): 1072 def inner_raise(): 1073 try: 1074 self.zero_div() 1075 except ZeroDivisionError as e: 1076 raise KeyError from e 1077 def outer_raise(): 1078 inner_raise() # Marker 1079 blocks = boundaries.split(self.get_report(outer_raise)) 1080 self.assertEqual(len(blocks), 3) 1081 self.assertEqual(blocks[1], cause_message) 1082 self.check_zero_div(blocks[0]) 1083 self.assertIn('inner_raise() # Marker', blocks[2]) 1084 1085 def test_context(self): 1086 def inner_raise(): 1087 try: 1088 self.zero_div() 1089 except ZeroDivisionError: 1090 raise KeyError 1091 def outer_raise(): 1092 inner_raise() # Marker 1093 blocks = boundaries.split(self.get_report(outer_raise)) 1094 self.assertEqual(len(blocks), 3) 1095 self.assertEqual(blocks[1], context_message) 1096 self.check_zero_div(blocks[0]) 1097 self.assertIn('inner_raise() # Marker', blocks[2]) 1098 1099 def test_context_suppression(self): 1100 try: 1101 try: 1102 raise Exception 1103 except: 1104 raise ZeroDivisionError from None 1105 except ZeroDivisionError as _: 1106 e = _ 1107 lines = self.get_report(e).splitlines() 1108 if has_no_debug_ranges(): 1109 self.assertEqual(len(lines), 4) 1110 self.assertTrue(lines[3].startswith('ZeroDivisionError')) 1111 else: 1112 self.assertEqual(len(lines), 5) 1113 self.assertTrue(lines[4].startswith('ZeroDivisionError')) 1114 self.assertTrue(lines[0].startswith('Traceback')) 1115 self.assertTrue(lines[1].startswith(' File')) 1116 self.assertIn('ZeroDivisionError from None', lines[2]) 1117 1118 def test_cause_and_context(self): 1119 # When both a cause and a context are set, only the cause should be 1120 # displayed and the context should be muted. 1121 def inner_raise(): 1122 try: 1123 self.zero_div() 1124 except ZeroDivisionError as _e: 1125 e = _e 1126 try: 1127 xyzzy 1128 except NameError: 1129 raise KeyError from e 1130 def outer_raise(): 1131 inner_raise() # Marker 1132 blocks = boundaries.split(self.get_report(outer_raise)) 1133 self.assertEqual(len(blocks), 3) 1134 self.assertEqual(blocks[1], cause_message) 1135 self.check_zero_div(blocks[0]) 1136 self.assertIn('inner_raise() # Marker', blocks[2]) 1137 1138 def test_cause_recursive(self): 1139 def inner_raise(): 1140 try: 1141 try: 1142 self.zero_div() 1143 except ZeroDivisionError as e: 1144 z = e 1145 raise KeyError from e 1146 except KeyError as e: 1147 raise z from e 1148 def outer_raise(): 1149 inner_raise() # Marker 1150 blocks = boundaries.split(self.get_report(outer_raise)) 1151 self.assertEqual(len(blocks), 3) 1152 self.assertEqual(blocks[1], cause_message) 1153 # The first block is the KeyError raised from the ZeroDivisionError 1154 self.assertIn('raise KeyError from e', blocks[0]) 1155 self.assertNotIn('1/0', blocks[0]) 1156 # The second block (apart from the boundary) is the ZeroDivisionError 1157 # re-raised from the KeyError 1158 self.assertIn('inner_raise() # Marker', blocks[2]) 1159 self.check_zero_div(blocks[2]) 1160 1161 def test_syntax_error_offset_at_eol(self): 1162 # See #10186. 1163 def e(): 1164 raise SyntaxError('', ('', 0, 5, 'hello')) 1165 msg = self.get_report(e).splitlines() 1166 self.assertEqual(msg[-2], " ^") 1167 def e(): 1168 exec("x = 5 | 4 |") 1169 msg = self.get_report(e).splitlines() 1170 self.assertEqual(msg[-2], ' ^') 1171 1172 def test_syntax_error_no_lineno(self): 1173 # See #34463. 1174 1175 # Without filename 1176 e = SyntaxError('bad syntax') 1177 msg = self.get_report(e).splitlines() 1178 self.assertEqual(msg, 1179 ['SyntaxError: bad syntax']) 1180 e.lineno = 100 1181 msg = self.get_report(e).splitlines() 1182 self.assertEqual(msg, 1183 [' File "<string>", line 100', 'SyntaxError: bad syntax']) 1184 1185 # With filename 1186 e = SyntaxError('bad syntax') 1187 e.filename = 'myfile.py' 1188 1189 msg = self.get_report(e).splitlines() 1190 self.assertEqual(msg, 1191 ['SyntaxError: bad syntax (myfile.py)']) 1192 e.lineno = 100 1193 msg = self.get_report(e).splitlines() 1194 self.assertEqual(msg, 1195 [' File "myfile.py", line 100', 'SyntaxError: bad syntax']) 1196 1197 def test_message_none(self): 1198 # A message that looks like "None" should not be treated specially 1199 err = self.get_report(Exception(None)) 1200 self.assertIn('Exception: None\n', err) 1201 err = self.get_report(Exception('None')) 1202 self.assertIn('Exception: None\n', err) 1203 err = self.get_report(Exception()) 1204 self.assertIn('Exception\n', err) 1205 err = self.get_report(Exception('')) 1206 self.assertIn('Exception\n', err) 1207 1208 def test_syntax_error_various_offsets(self): 1209 for offset in range(-5, 10): 1210 for add in [0, 2]: 1211 text = " "*add + "text%d" % offset 1212 expected = [' File "file.py", line 1'] 1213 if offset < 1: 1214 expected.append(" %s" % text.lstrip()) 1215 elif offset <= 6: 1216 expected.append(" %s" % text.lstrip()) 1217 expected.append(" %s^" % (" "*(offset-1))) 1218 else: 1219 expected.append(" %s" % text.lstrip()) 1220 expected.append(" %s^" % (" "*5)) 1221 expected.append("SyntaxError: msg") 1222 expected.append("") 1223 err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text))) 1224 exp = "\n".join(expected) 1225 self.assertEqual(exp, err) 1226 1227 def test_exception_with_note(self): 1228 e = ValueError(42) 1229 vanilla = self.get_report(e) 1230 1231 e.__note__ = 'My Note' 1232 self.assertEqual(self.get_report(e), vanilla + 'My Note\n') 1233 1234 e.__note__ = '' 1235 self.assertEqual(self.get_report(e), vanilla + '\n') 1236 1237 e.__note__ = 'Your Note' 1238 self.assertEqual(self.get_report(e), vanilla + 'Your Note\n') 1239 1240 e.__note__ = None 1241 self.assertEqual(self.get_report(e), vanilla) 1242 1243 def test_exception_qualname(self): 1244 class A: 1245 class B: 1246 class X(Exception): 1247 def __str__(self): 1248 return "I am X" 1249 1250 err = self.get_report(A.B.X()) 1251 str_value = 'I am X' 1252 str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__]) 1253 exp = "%s: %s\n" % (str_name, str_value) 1254 self.assertEqual(exp, err) 1255 1256 def test_exception_modulename(self): 1257 class X(Exception): 1258 def __str__(self): 1259 return "I am X" 1260 1261 for modulename in '__main__', 'builtins', 'some_module': 1262 X.__module__ = modulename 1263 with self.subTest(modulename=modulename): 1264 err = self.get_report(X()) 1265 str_value = 'I am X' 1266 if modulename in ['builtins', '__main__']: 1267 str_name = X.__qualname__ 1268 else: 1269 str_name = '.'.join([X.__module__, X.__qualname__]) 1270 exp = "%s: %s\n" % (str_name, str_value) 1271 self.assertEqual(exp, err) 1272 1273 def test_exception_modulename_not_unicode(self): 1274 class X(Exception): 1275 def __str__(self): 1276 return "I am X" 1277 1278 X.__module__ = 42 1279 1280 err = self.get_report(X()) 1281 exp = f'<unknown>.{X.__qualname__}: I am X\n' 1282 self.assertEqual(exp, err) 1283 1284 def test_exception_bad__str__(self): 1285 class X(Exception): 1286 def __str__(self): 1287 1/0 1288 err = self.get_report(X()) 1289 str_value = '<exception str() failed>' 1290 str_name = '.'.join([X.__module__, X.__qualname__]) 1291 self.assertEqual(err, f"{str_name}: {str_value}\n") 1292 1293 1294 # #### Exception Groups #### 1295 1296 def test_exception_group_basic(self): 1297 def exc(): 1298 raise ExceptionGroup("eg", [ValueError(1), TypeError(2)]) 1299 1300 expected = ( 1301 f' + Exception Group Traceback (most recent call last):\n' 1302 f' | File "{__file__}", line {self.callable_line}, in get_exception\n' 1303 f' | exception_or_callable()\n' 1304 f' | ^^^^^^^^^^^^^^^^^^^^^^^\n' 1305 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n' 1306 f' | raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n' 1307 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1308 f' | ExceptionGroup: eg\n' 1309 f' +-+---------------- 1 ----------------\n' 1310 f' | ValueError: 1\n' 1311 f' +---------------- 2 ----------------\n' 1312 f' | TypeError: 2\n' 1313 f' +------------------------------------\n') 1314 1315 report = self.get_report(exc) 1316 self.assertEqual(report, expected) 1317 1318 def test_exception_group_cause(self): 1319 def exc(): 1320 EG = ExceptionGroup 1321 try: 1322 raise EG("eg1", [ValueError(1), TypeError(2)]) 1323 except Exception as e: 1324 raise EG("eg2", [ValueError(3), TypeError(4)]) from e 1325 1326 expected = (f' + Exception Group Traceback (most recent call last):\n' 1327 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n' 1328 f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n' 1329 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1330 f' | ExceptionGroup: eg1\n' 1331 f' +-+---------------- 1 ----------------\n' 1332 f' | ValueError: 1\n' 1333 f' +---------------- 2 ----------------\n' 1334 f' | TypeError: 2\n' 1335 f' +------------------------------------\n' 1336 f'\n' 1337 f'The above exception was the direct cause of the following exception:\n' 1338 f'\n' 1339 f' + Exception Group Traceback (most recent call last):\n' 1340 f' | File "{__file__}", line {self.callable_line}, in get_exception\n' 1341 f' | exception_or_callable()\n' 1342 f' | ^^^^^^^^^^^^^^^^^^^^^^^\n' 1343 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' 1344 f' | raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n' 1345 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1346 f' | ExceptionGroup: eg2\n' 1347 f' +-+---------------- 1 ----------------\n' 1348 f' | ValueError: 3\n' 1349 f' +---------------- 2 ----------------\n' 1350 f' | TypeError: 4\n' 1351 f' +------------------------------------\n') 1352 1353 report = self.get_report(exc) 1354 self.assertEqual(report, expected) 1355 1356 def test_exception_group_context_with_context(self): 1357 def exc(): 1358 EG = ExceptionGroup 1359 try: 1360 try: 1361 raise EG("eg1", [ValueError(1), TypeError(2)]) 1362 except: 1363 raise EG("eg2", [ValueError(3), TypeError(4)]) 1364 except: 1365 raise ImportError(5) 1366 1367 expected = ( 1368 f' + Exception Group Traceback (most recent call last):\n' 1369 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 4}, in exc\n' 1370 f' | raise EG("eg1", [ValueError(1), TypeError(2)])\n' 1371 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1372 f' | ExceptionGroup: eg1\n' 1373 f' +-+---------------- 1 ----------------\n' 1374 f' | ValueError: 1\n' 1375 f' +---------------- 2 ----------------\n' 1376 f' | TypeError: 2\n' 1377 f' +------------------------------------\n' 1378 f'\n' 1379 f'During handling of the above exception, another exception occurred:\n' 1380 f'\n' 1381 f' + Exception Group Traceback (most recent call last):\n' 1382 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n' 1383 f' | raise EG("eg2", [ValueError(3), TypeError(4)])\n' 1384 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1385 f' | ExceptionGroup: eg2\n' 1386 f' +-+---------------- 1 ----------------\n' 1387 f' | ValueError: 3\n' 1388 f' +---------------- 2 ----------------\n' 1389 f' | TypeError: 4\n' 1390 f' +------------------------------------\n' 1391 f'\n' 1392 f'During handling of the above exception, another exception occurred:\n' 1393 f'\n' 1394 f'Traceback (most recent call last):\n' 1395 f' File "{__file__}", line {self.callable_line}, in get_exception\n' 1396 f' exception_or_callable()\n' 1397 f' ^^^^^^^^^^^^^^^^^^^^^^^\n' 1398 f' File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n' 1399 f' raise ImportError(5)\n' 1400 f' ^^^^^^^^^^^^^^^^^^^^\n' 1401 f'ImportError: 5\n') 1402 1403 report = self.get_report(exc) 1404 self.assertEqual(report, expected) 1405 1406 def test_exception_group_nested(self): 1407 def exc(): 1408 EG = ExceptionGroup 1409 VE = ValueError 1410 TE = TypeError 1411 try: 1412 try: 1413 raise EG("nested", [TE(2), TE(3)]) 1414 except Exception as e: 1415 exc = e 1416 raise EG("eg", [VE(1), exc, VE(4)]) 1417 except: 1418 raise EG("top", [VE(5)]) 1419 1420 expected = (f' + Exception Group Traceback (most recent call last):\n' 1421 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n' 1422 f' | raise EG("eg", [VE(1), exc, VE(4)])\n' 1423 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1424 f' | ExceptionGroup: eg\n' 1425 f' +-+---------------- 1 ----------------\n' 1426 f' | ValueError: 1\n' 1427 f' +---------------- 2 ----------------\n' 1428 f' | Exception Group Traceback (most recent call last):\n' 1429 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n' 1430 f' | raise EG("nested", [TE(2), TE(3)])\n' 1431 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1432 f' | ExceptionGroup: nested\n' 1433 f' +-+---------------- 1 ----------------\n' 1434 f' | TypeError: 2\n' 1435 f' +---------------- 2 ----------------\n' 1436 f' | TypeError: 3\n' 1437 f' +------------------------------------\n' 1438 f' +---------------- 3 ----------------\n' 1439 f' | ValueError: 4\n' 1440 f' +------------------------------------\n' 1441 f'\n' 1442 f'During handling of the above exception, another exception occurred:\n' 1443 f'\n' 1444 f' + Exception Group Traceback (most recent call last):\n' 1445 f' | File "{__file__}", line {self.callable_line}, in get_exception\n' 1446 f' | exception_or_callable()\n' 1447 f' | ^^^^^^^^^^^^^^^^^^^^^^^\n' 1448 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n' 1449 f' | raise EG("top", [VE(5)])\n' 1450 f' | ^^^^^^^^^^^^^^^^^^^^^^^^\n' 1451 f' | ExceptionGroup: top\n' 1452 f' +-+---------------- 1 ----------------\n' 1453 f' | ValueError: 5\n' 1454 f' +------------------------------------\n') 1455 1456 report = self.get_report(exc) 1457 self.assertEqual(report, expected) 1458 1459 def test_exception_group_width_limit(self): 1460 excs = [] 1461 for i in range(1000): 1462 excs.append(ValueError(i)) 1463 eg = ExceptionGroup('eg', excs) 1464 1465 expected = (' | ExceptionGroup: eg\n' 1466 ' +-+---------------- 1 ----------------\n' 1467 ' | ValueError: 0\n' 1468 ' +---------------- 2 ----------------\n' 1469 ' | ValueError: 1\n' 1470 ' +---------------- 3 ----------------\n' 1471 ' | ValueError: 2\n' 1472 ' +---------------- 4 ----------------\n' 1473 ' | ValueError: 3\n' 1474 ' +---------------- 5 ----------------\n' 1475 ' | ValueError: 4\n' 1476 ' +---------------- 6 ----------------\n' 1477 ' | ValueError: 5\n' 1478 ' +---------------- 7 ----------------\n' 1479 ' | ValueError: 6\n' 1480 ' +---------------- 8 ----------------\n' 1481 ' | ValueError: 7\n' 1482 ' +---------------- 9 ----------------\n' 1483 ' | ValueError: 8\n' 1484 ' +---------------- 10 ----------------\n' 1485 ' | ValueError: 9\n' 1486 ' +---------------- 11 ----------------\n' 1487 ' | ValueError: 10\n' 1488 ' +---------------- 12 ----------------\n' 1489 ' | ValueError: 11\n' 1490 ' +---------------- 13 ----------------\n' 1491 ' | ValueError: 12\n' 1492 ' +---------------- 14 ----------------\n' 1493 ' | ValueError: 13\n' 1494 ' +---------------- 15 ----------------\n' 1495 ' | ValueError: 14\n' 1496 ' +---------------- ... ----------------\n' 1497 ' | and 985 more exceptions\n' 1498 ' +------------------------------------\n') 1499 1500 report = self.get_report(eg) 1501 self.assertEqual(report, expected) 1502 1503 def test_exception_group_depth_limit(self): 1504 exc = TypeError('bad type') 1505 for i in range(1000): 1506 exc = ExceptionGroup( 1507 f'eg{i}', 1508 [ValueError(i), exc, ValueError(-i)]) 1509 1510 expected = (' | ExceptionGroup: eg999\n' 1511 ' +-+---------------- 1 ----------------\n' 1512 ' | ValueError: 999\n' 1513 ' +---------------- 2 ----------------\n' 1514 ' | ExceptionGroup: eg998\n' 1515 ' +-+---------------- 1 ----------------\n' 1516 ' | ValueError: 998\n' 1517 ' +---------------- 2 ----------------\n' 1518 ' | ExceptionGroup: eg997\n' 1519 ' +-+---------------- 1 ----------------\n' 1520 ' | ValueError: 997\n' 1521 ' +---------------- 2 ----------------\n' 1522 ' | ExceptionGroup: eg996\n' 1523 ' +-+---------------- 1 ----------------\n' 1524 ' | ValueError: 996\n' 1525 ' +---------------- 2 ----------------\n' 1526 ' | ExceptionGroup: eg995\n' 1527 ' +-+---------------- 1 ----------------\n' 1528 ' | ValueError: 995\n' 1529 ' +---------------- 2 ----------------\n' 1530 ' | ExceptionGroup: eg994\n' 1531 ' +-+---------------- 1 ----------------\n' 1532 ' | ValueError: 994\n' 1533 ' +---------------- 2 ----------------\n' 1534 ' | ExceptionGroup: eg993\n' 1535 ' +-+---------------- 1 ----------------\n' 1536 ' | ValueError: 993\n' 1537 ' +---------------- 2 ----------------\n' 1538 ' | ExceptionGroup: eg992\n' 1539 ' +-+---------------- 1 ----------------\n' 1540 ' | ValueError: 992\n' 1541 ' +---------------- 2 ----------------\n' 1542 ' | ExceptionGroup: eg991\n' 1543 ' +-+---------------- 1 ----------------\n' 1544 ' | ValueError: 991\n' 1545 ' +---------------- 2 ----------------\n' 1546 ' | ExceptionGroup: eg990\n' 1547 ' +-+---------------- 1 ----------------\n' 1548 ' | ValueError: 990\n' 1549 ' +---------------- 2 ----------------\n' 1550 ' | ... (max_group_depth is 10)\n' 1551 ' +---------------- 3 ----------------\n' 1552 ' | ValueError: -990\n' 1553 ' +------------------------------------\n' 1554 ' +---------------- 3 ----------------\n' 1555 ' | ValueError: -991\n' 1556 ' +------------------------------------\n' 1557 ' +---------------- 3 ----------------\n' 1558 ' | ValueError: -992\n' 1559 ' +------------------------------------\n' 1560 ' +---------------- 3 ----------------\n' 1561 ' | ValueError: -993\n' 1562 ' +------------------------------------\n' 1563 ' +---------------- 3 ----------------\n' 1564 ' | ValueError: -994\n' 1565 ' +------------------------------------\n' 1566 ' +---------------- 3 ----------------\n' 1567 ' | ValueError: -995\n' 1568 ' +------------------------------------\n' 1569 ' +---------------- 3 ----------------\n' 1570 ' | ValueError: -996\n' 1571 ' +------------------------------------\n' 1572 ' +---------------- 3 ----------------\n' 1573 ' | ValueError: -997\n' 1574 ' +------------------------------------\n' 1575 ' +---------------- 3 ----------------\n' 1576 ' | ValueError: -998\n' 1577 ' +------------------------------------\n' 1578 ' +---------------- 3 ----------------\n' 1579 ' | ValueError: -999\n' 1580 ' +------------------------------------\n') 1581 1582 report = self.get_report(exc) 1583 self.assertEqual(report, expected) 1584 1585 def test_exception_group_with_notes(self): 1586 def exc(): 1587 try: 1588 excs = [] 1589 for msg in ['bad value', 'terrible value']: 1590 try: 1591 raise ValueError(msg) 1592 except ValueError as e: 1593 e.__note__ = f'the {msg}' 1594 excs.append(e) 1595 raise ExceptionGroup("nested", excs) 1596 except ExceptionGroup as e: 1597 e.__note__ = ('>> Multi line note\n' 1598 '>> Because I am such\n' 1599 '>> an important exception.\n' 1600 '>> empty lines work too\n' 1601 '\n' 1602 '(that was an empty line)') 1603 raise 1604 1605 expected = (f' + Exception Group Traceback (most recent call last):\n' 1606 f' | File "{__file__}", line {self.callable_line}, in get_exception\n' 1607 f' | exception_or_callable()\n' 1608 f' | ^^^^^^^^^^^^^^^^^^^^^^^\n' 1609 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n' 1610 f' | raise ExceptionGroup("nested", excs)\n' 1611 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 1612 f' | ExceptionGroup: nested\n' 1613 f' | >> Multi line note\n' 1614 f' | >> Because I am such\n' 1615 f' | >> an important exception.\n' 1616 f' | >> empty lines work too\n' 1617 f' | \n' 1618 f' | (that was an empty line)\n' 1619 f' +-+---------------- 1 ----------------\n' 1620 f' | Traceback (most recent call last):\n' 1621 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' 1622 f' | raise ValueError(msg)\n' 1623 f' | ^^^^^^^^^^^^^^^^^^^^^\n' 1624 f' | ValueError: bad value\n' 1625 f' | the bad value\n' 1626 f' +---------------- 2 ----------------\n' 1627 f' | Traceback (most recent call last):\n' 1628 f' | File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n' 1629 f' | raise ValueError(msg)\n' 1630 f' | ^^^^^^^^^^^^^^^^^^^^^\n' 1631 f' | ValueError: terrible value\n' 1632 f' | the terrible value\n' 1633 f' +------------------------------------\n') 1634 1635 report = self.get_report(exc) 1636 self.assertEqual(report, expected) 1637 1638 1639class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): 1640 # 1641 # This checks reporting through the 'traceback' module, with both 1642 # format_exception() and print_exception(). 1643 # 1644 1645 def get_report(self, e): 1646 e = self.get_exception(e) 1647 s = ''.join( 1648 traceback.format_exception(type(e), e, e.__traceback__)) 1649 with captured_output("stderr") as sio: 1650 traceback.print_exception(type(e), e, e.__traceback__) 1651 self.assertEqual(sio.getvalue(), s) 1652 return s 1653 1654 1655class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): 1656 # 1657 # This checks built-in reporting by the interpreter. 1658 # 1659 1660 @cpython_only 1661 def get_report(self, e): 1662 from _testcapi import exception_print 1663 e = self.get_exception(e) 1664 with captured_output("stderr") as s: 1665 exception_print(e) 1666 return s.getvalue() 1667 1668 1669class LimitTests(unittest.TestCase): 1670 1671 ''' Tests for limit argument. 1672 It's enough to test extact_tb, extract_stack and format_exception ''' 1673 1674 def last_raises1(self): 1675 raise Exception('Last raised') 1676 1677 def last_raises2(self): 1678 self.last_raises1() 1679 1680 def last_raises3(self): 1681 self.last_raises2() 1682 1683 def last_raises4(self): 1684 self.last_raises3() 1685 1686 def last_raises5(self): 1687 self.last_raises4() 1688 1689 def last_returns_frame1(self): 1690 return sys._getframe() 1691 1692 def last_returns_frame2(self): 1693 return self.last_returns_frame1() 1694 1695 def last_returns_frame3(self): 1696 return self.last_returns_frame2() 1697 1698 def last_returns_frame4(self): 1699 return self.last_returns_frame3() 1700 1701 def last_returns_frame5(self): 1702 return self.last_returns_frame4() 1703 1704 def test_extract_stack(self): 1705 frame = self.last_returns_frame5() 1706 def extract(**kwargs): 1707 return traceback.extract_stack(frame, **kwargs) 1708 def assertEqualExcept(actual, expected, ignore): 1709 self.assertEqual(actual[:ignore], expected[:ignore]) 1710 self.assertEqual(actual[ignore+1:], expected[ignore+1:]) 1711 self.assertEqual(len(actual), len(expected)) 1712 1713 with support.swap_attr(sys, 'tracebacklimit', 1000): 1714 nolim = extract() 1715 self.assertGreater(len(nolim), 5) 1716 self.assertEqual(extract(limit=2), nolim[-2:]) 1717 assertEqualExcept(extract(limit=100), nolim[-100:], -5-1) 1718 self.assertEqual(extract(limit=-2), nolim[:2]) 1719 assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1) 1720 self.assertEqual(extract(limit=0), []) 1721 del sys.tracebacklimit 1722 assertEqualExcept(extract(), nolim, -5-1) 1723 sys.tracebacklimit = 2 1724 self.assertEqual(extract(), nolim[-2:]) 1725 self.assertEqual(extract(limit=3), nolim[-3:]) 1726 self.assertEqual(extract(limit=-3), nolim[:3]) 1727 sys.tracebacklimit = 0 1728 self.assertEqual(extract(), []) 1729 sys.tracebacklimit = -1 1730 self.assertEqual(extract(), []) 1731 1732 def test_extract_tb(self): 1733 try: 1734 self.last_raises5() 1735 except Exception: 1736 exc_type, exc_value, tb = sys.exc_info() 1737 def extract(**kwargs): 1738 return traceback.extract_tb(tb, **kwargs) 1739 1740 with support.swap_attr(sys, 'tracebacklimit', 1000): 1741 nolim = extract() 1742 self.assertEqual(len(nolim), 5+1) 1743 self.assertEqual(extract(limit=2), nolim[:2]) 1744 self.assertEqual(extract(limit=10), nolim) 1745 self.assertEqual(extract(limit=-2), nolim[-2:]) 1746 self.assertEqual(extract(limit=-10), nolim) 1747 self.assertEqual(extract(limit=0), []) 1748 del sys.tracebacklimit 1749 self.assertEqual(extract(), nolim) 1750 sys.tracebacklimit = 2 1751 self.assertEqual(extract(), nolim[:2]) 1752 self.assertEqual(extract(limit=3), nolim[:3]) 1753 self.assertEqual(extract(limit=-3), nolim[-3:]) 1754 sys.tracebacklimit = 0 1755 self.assertEqual(extract(), []) 1756 sys.tracebacklimit = -1 1757 self.assertEqual(extract(), []) 1758 1759 def test_format_exception(self): 1760 try: 1761 self.last_raises5() 1762 except Exception: 1763 exc_type, exc_value, tb = sys.exc_info() 1764 # [1:-1] to exclude "Traceback (...)" header and 1765 # exception type and value 1766 def extract(**kwargs): 1767 return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1] 1768 1769 with support.swap_attr(sys, 'tracebacklimit', 1000): 1770 nolim = extract() 1771 self.assertEqual(len(nolim), 5+1) 1772 self.assertEqual(extract(limit=2), nolim[:2]) 1773 self.assertEqual(extract(limit=10), nolim) 1774 self.assertEqual(extract(limit=-2), nolim[-2:]) 1775 self.assertEqual(extract(limit=-10), nolim) 1776 self.assertEqual(extract(limit=0), []) 1777 del sys.tracebacklimit 1778 self.assertEqual(extract(), nolim) 1779 sys.tracebacklimit = 2 1780 self.assertEqual(extract(), nolim[:2]) 1781 self.assertEqual(extract(limit=3), nolim[:3]) 1782 self.assertEqual(extract(limit=-3), nolim[-3:]) 1783 sys.tracebacklimit = 0 1784 self.assertEqual(extract(), []) 1785 sys.tracebacklimit = -1 1786 self.assertEqual(extract(), []) 1787 1788 1789class MiscTracebackCases(unittest.TestCase): 1790 # 1791 # Check non-printing functions in traceback module 1792 # 1793 1794 def test_clear(self): 1795 def outer(): 1796 middle() 1797 def middle(): 1798 inner() 1799 def inner(): 1800 i = 1 1801 1/0 1802 1803 try: 1804 outer() 1805 except: 1806 type_, value, tb = sys.exc_info() 1807 1808 # Initial assertion: there's one local in the inner frame. 1809 inner_frame = tb.tb_next.tb_next.tb_next.tb_frame 1810 self.assertEqual(len(inner_frame.f_locals), 1) 1811 1812 # Clear traceback frames 1813 traceback.clear_frames(tb) 1814 1815 # Local variable dict should now be empty. 1816 self.assertEqual(len(inner_frame.f_locals), 0) 1817 1818 def test_extract_stack(self): 1819 def extract(): 1820 return traceback.extract_stack() 1821 result = extract() 1822 lineno = extract.__code__.co_firstlineno 1823 self.assertEqual(result[-2:], [ 1824 (__file__, lineno+2, 'test_extract_stack', 'result = extract()'), 1825 (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'), 1826 ]) 1827 self.assertEqual(len(result[0]), 4) 1828 1829 1830class TestFrame(unittest.TestCase): 1831 1832 def test_basics(self): 1833 linecache.clearcache() 1834 linecache.lazycache("f", globals()) 1835 f = traceback.FrameSummary("f", 1, "dummy") 1836 self.assertEqual(f, 1837 ("f", 1, "dummy", '"""Test cases for traceback module"""')) 1838 self.assertEqual(tuple(f), 1839 ("f", 1, "dummy", '"""Test cases for traceback module"""')) 1840 self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy")) 1841 self.assertEqual(f, tuple(f)) 1842 # Since tuple.__eq__ doesn't support FrameSummary, the equality 1843 # operator fallbacks to FrameSummary.__eq__. 1844 self.assertEqual(tuple(f), f) 1845 self.assertIsNone(f.locals) 1846 self.assertNotEqual(f, object()) 1847 self.assertEqual(f, ALWAYS_EQ) 1848 1849 def test_lazy_lines(self): 1850 linecache.clearcache() 1851 f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) 1852 self.assertEqual(None, f._line) 1853 linecache.lazycache("f", globals()) 1854 self.assertEqual( 1855 '"""Test cases for traceback module"""', 1856 f.line) 1857 1858 def test_no_line(self): 1859 f = traceback.FrameSummary("f", None, "dummy") 1860 self.assertEqual(f.line, None) 1861 1862 def test_explicit_line(self): 1863 f = traceback.FrameSummary("f", 1, "dummy", line="line") 1864 self.assertEqual("line", f.line) 1865 1866 def test_len(self): 1867 f = traceback.FrameSummary("f", 1, "dummy", line="line") 1868 self.assertEqual(len(f), 4) 1869 1870 1871class TestStack(unittest.TestCase): 1872 1873 def test_walk_stack(self): 1874 def deeper(): 1875 return list(traceback.walk_stack(None)) 1876 s1 = list(traceback.walk_stack(None)) 1877 s2 = deeper() 1878 self.assertEqual(len(s2) - len(s1), 1) 1879 self.assertEqual(s2[1:], s1) 1880 1881 def test_walk_tb(self): 1882 try: 1883 1/0 1884 except Exception: 1885 _, _, tb = sys.exc_info() 1886 s = list(traceback.walk_tb(tb)) 1887 self.assertEqual(len(s), 1) 1888 1889 def test_extract_stack(self): 1890 s = traceback.StackSummary.extract(traceback.walk_stack(None)) 1891 self.assertIsInstance(s, traceback.StackSummary) 1892 1893 def test_extract_stack_limit(self): 1894 s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5) 1895 self.assertEqual(len(s), 5) 1896 1897 def test_extract_stack_lookup_lines(self): 1898 linecache.clearcache() 1899 linecache.updatecache('/foo.py', globals()) 1900 c = test_code('/foo.py', 'method') 1901 f = test_frame(c, None, None) 1902 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True) 1903 linecache.clearcache() 1904 self.assertEqual(s[0].line, "import sys") 1905 1906 def test_extract_stackup_deferred_lookup_lines(self): 1907 linecache.clearcache() 1908 c = test_code('/foo.py', 'method') 1909 f = test_frame(c, None, None) 1910 s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False) 1911 self.assertEqual({}, linecache.cache) 1912 linecache.updatecache('/foo.py', globals()) 1913 self.assertEqual(s[0].line, "import sys") 1914 1915 def test_from_list(self): 1916 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 1917 self.assertEqual( 1918 [' File "foo.py", line 1, in fred\n line\n'], 1919 s.format()) 1920 1921 def test_from_list_edited_stack(self): 1922 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 1923 s[0] = ('foo.py', 2, 'fred', 'line') 1924 s2 = traceback.StackSummary.from_list(s) 1925 self.assertEqual( 1926 [' File "foo.py", line 2, in fred\n line\n'], 1927 s2.format()) 1928 1929 def test_format_smoke(self): 1930 # For detailed tests see the format_list tests, which consume the same 1931 # code. 1932 s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) 1933 self.assertEqual( 1934 [' File "foo.py", line 1, in fred\n line\n'], 1935 s.format()) 1936 1937 def test_locals(self): 1938 linecache.updatecache('/foo.py', globals()) 1939 c = test_code('/foo.py', 'method') 1940 f = test_frame(c, globals(), {'something': 1}) 1941 s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True) 1942 self.assertEqual(s[0].locals, {'something': '1'}) 1943 1944 def test_no_locals(self): 1945 linecache.updatecache('/foo.py', globals()) 1946 c = test_code('/foo.py', 'method') 1947 f = test_frame(c, globals(), {'something': 1}) 1948 s = traceback.StackSummary.extract(iter([(f, 6)])) 1949 self.assertEqual(s[0].locals, None) 1950 1951 def test_format_locals(self): 1952 def some_inner(k, v): 1953 a = 1 1954 b = 2 1955 return traceback.StackSummary.extract( 1956 traceback.walk_stack(None), capture_locals=True, limit=1) 1957 s = some_inner(3, 4) 1958 self.assertEqual( 1959 [' File "%s", line %d, in some_inner\n' 1960 ' return traceback.StackSummary.extract(\n' 1961 ' a = 1\n' 1962 ' b = 2\n' 1963 ' k = 3\n' 1964 ' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3) 1965 ], s.format()) 1966 1967 def test_custom_format_frame(self): 1968 class CustomStackSummary(traceback.StackSummary): 1969 def format_frame_summary(self, frame_summary): 1970 return f'{frame_summary.filename}:{frame_summary.lineno}' 1971 1972 def some_inner(): 1973 return CustomStackSummary.extract( 1974 traceback.walk_stack(None), limit=1) 1975 1976 s = some_inner() 1977 self.assertEqual( 1978 s.format(), 1979 [f'{__file__}:{some_inner.__code__.co_firstlineno + 1}']) 1980 1981 def test_dropping_frames(self): 1982 def f(): 1983 1/0 1984 1985 def g(): 1986 try: 1987 f() 1988 except: 1989 return sys.exc_info() 1990 1991 exc_info = g() 1992 1993 class Skip_G(traceback.StackSummary): 1994 def format_frame_summary(self, frame_summary): 1995 if frame_summary.name == 'g': 1996 return None 1997 return super().format_frame_summary(frame_summary) 1998 1999 stack = Skip_G.extract( 2000 traceback.walk_tb(exc_info[2])).format() 2001 2002 self.assertEqual(len(stack), 1) 2003 lno = f.__code__.co_firstlineno + 1 2004 self.assertEqual( 2005 stack[0], 2006 f' File "{__file__}", line {lno}, in f\n 1/0\n' 2007 ) 2008 2009 2010class TestTracebackException(unittest.TestCase): 2011 2012 def test_smoke(self): 2013 try: 2014 1/0 2015 except Exception: 2016 exc_info = sys.exc_info() 2017 exc = traceback.TracebackException(*exc_info) 2018 expected_stack = traceback.StackSummary.extract( 2019 traceback.walk_tb(exc_info[2])) 2020 self.assertEqual(None, exc.__cause__) 2021 self.assertEqual(None, exc.__context__) 2022 self.assertEqual(False, exc.__suppress_context__) 2023 self.assertEqual(expected_stack, exc.stack) 2024 self.assertEqual(exc_info[0], exc.exc_type) 2025 self.assertEqual(str(exc_info[1]), str(exc)) 2026 2027 def test_from_exception(self): 2028 # Check all the parameters are accepted. 2029 def foo(): 2030 1/0 2031 try: 2032 foo() 2033 except Exception as e: 2034 exc_info = sys.exc_info() 2035 self.expected_stack = traceback.StackSummary.extract( 2036 traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, 2037 capture_locals=True) 2038 self.exc = traceback.TracebackException.from_exception( 2039 e, limit=1, lookup_lines=False, capture_locals=True) 2040 expected_stack = self.expected_stack 2041 exc = self.exc 2042 self.assertEqual(None, exc.__cause__) 2043 self.assertEqual(None, exc.__context__) 2044 self.assertEqual(False, exc.__suppress_context__) 2045 self.assertEqual(expected_stack, exc.stack) 2046 self.assertEqual(exc_info[0], exc.exc_type) 2047 self.assertEqual(str(exc_info[1]), str(exc)) 2048 2049 def test_cause(self): 2050 try: 2051 try: 2052 1/0 2053 finally: 2054 exc_info_context = sys.exc_info() 2055 exc_context = traceback.TracebackException(*exc_info_context) 2056 cause = Exception("cause") 2057 raise Exception("uh oh") from cause 2058 except Exception: 2059 exc_info = sys.exc_info() 2060 exc = traceback.TracebackException(*exc_info) 2061 expected_stack = traceback.StackSummary.extract( 2062 traceback.walk_tb(exc_info[2])) 2063 exc_cause = traceback.TracebackException(Exception, cause, None) 2064 self.assertEqual(exc_cause, exc.__cause__) 2065 self.assertEqual(exc_context, exc.__context__) 2066 self.assertEqual(True, exc.__suppress_context__) 2067 self.assertEqual(expected_stack, exc.stack) 2068 self.assertEqual(exc_info[0], exc.exc_type) 2069 self.assertEqual(str(exc_info[1]), str(exc)) 2070 2071 def test_context(self): 2072 try: 2073 try: 2074 1/0 2075 finally: 2076 exc_info_context = sys.exc_info() 2077 exc_context = traceback.TracebackException(*exc_info_context) 2078 raise Exception("uh oh") 2079 except Exception: 2080 exc_info = sys.exc_info() 2081 exc = traceback.TracebackException(*exc_info) 2082 expected_stack = traceback.StackSummary.extract( 2083 traceback.walk_tb(exc_info[2])) 2084 self.assertEqual(None, exc.__cause__) 2085 self.assertEqual(exc_context, exc.__context__) 2086 self.assertEqual(False, exc.__suppress_context__) 2087 self.assertEqual(expected_stack, exc.stack) 2088 self.assertEqual(exc_info[0], exc.exc_type) 2089 self.assertEqual(str(exc_info[1]), str(exc)) 2090 2091 def test_long_context_chain(self): 2092 def f(): 2093 try: 2094 1/0 2095 except: 2096 f() 2097 2098 try: 2099 f() 2100 except RecursionError: 2101 exc_info = sys.exc_info() 2102 else: 2103 self.fail("Exception not raised") 2104 2105 te = traceback.TracebackException(*exc_info) 2106 res = list(te.format()) 2107 2108 # many ZeroDiv errors followed by the RecursionError 2109 self.assertGreater(len(res), sys.getrecursionlimit()) 2110 self.assertGreater( 2111 len([l for l in res if 'ZeroDivisionError:' in l]), 2112 sys.getrecursionlimit() * 0.5) 2113 self.assertIn( 2114 "RecursionError: maximum recursion depth exceeded", res[-1]) 2115 2116 def test_compact_with_cause(self): 2117 try: 2118 try: 2119 1/0 2120 finally: 2121 cause = Exception("cause") 2122 raise Exception("uh oh") from cause 2123 except Exception: 2124 exc_info = sys.exc_info() 2125 exc = traceback.TracebackException(*exc_info, compact=True) 2126 expected_stack = traceback.StackSummary.extract( 2127 traceback.walk_tb(exc_info[2])) 2128 exc_cause = traceback.TracebackException(Exception, cause, None) 2129 self.assertEqual(exc_cause, exc.__cause__) 2130 self.assertEqual(None, exc.__context__) 2131 self.assertEqual(True, exc.__suppress_context__) 2132 self.assertEqual(expected_stack, exc.stack) 2133 self.assertEqual(exc_info[0], exc.exc_type) 2134 self.assertEqual(str(exc_info[1]), str(exc)) 2135 2136 def test_compact_no_cause(self): 2137 try: 2138 try: 2139 1/0 2140 finally: 2141 exc_info_context = sys.exc_info() 2142 exc_context = traceback.TracebackException(*exc_info_context) 2143 raise Exception("uh oh") 2144 except Exception: 2145 exc_info = sys.exc_info() 2146 exc = traceback.TracebackException(*exc_info, compact=True) 2147 expected_stack = traceback.StackSummary.extract( 2148 traceback.walk_tb(exc_info[2])) 2149 self.assertEqual(None, exc.__cause__) 2150 self.assertEqual(exc_context, exc.__context__) 2151 self.assertEqual(False, exc.__suppress_context__) 2152 self.assertEqual(expected_stack, exc.stack) 2153 self.assertEqual(exc_info[0], exc.exc_type) 2154 self.assertEqual(str(exc_info[1]), str(exc)) 2155 2156 def test_no_refs_to_exception_and_traceback_objects(self): 2157 try: 2158 1/0 2159 except Exception: 2160 exc_info = sys.exc_info() 2161 2162 refcnt1 = sys.getrefcount(exc_info[1]) 2163 refcnt2 = sys.getrefcount(exc_info[2]) 2164 exc = traceback.TracebackException(*exc_info) 2165 self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1) 2166 self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2) 2167 2168 def test_comparison_basic(self): 2169 try: 2170 1/0 2171 except Exception: 2172 exc_info = sys.exc_info() 2173 exc = traceback.TracebackException(*exc_info) 2174 exc2 = traceback.TracebackException(*exc_info) 2175 self.assertIsNot(exc, exc2) 2176 self.assertEqual(exc, exc2) 2177 self.assertNotEqual(exc, object()) 2178 self.assertEqual(exc, ALWAYS_EQ) 2179 2180 def test_comparison_params_variations(self): 2181 def raise_exc(): 2182 try: 2183 raise ValueError('bad value') 2184 except: 2185 raise 2186 2187 def raise_with_locals(): 2188 x, y = 1, 2 2189 raise_exc() 2190 2191 try: 2192 raise_with_locals() 2193 except Exception: 2194 exc_info = sys.exc_info() 2195 2196 exc = traceback.TracebackException(*exc_info) 2197 exc1 = traceback.TracebackException(*exc_info, limit=10) 2198 exc2 = traceback.TracebackException(*exc_info, limit=2) 2199 2200 self.assertEqual(exc, exc1) # limit=10 gets all frames 2201 self.assertNotEqual(exc, exc2) # limit=2 truncates the output 2202 2203 # locals change the output 2204 exc3 = traceback.TracebackException(*exc_info, capture_locals=True) 2205 self.assertNotEqual(exc, exc3) 2206 2207 # there are no locals in the innermost frame 2208 exc4 = traceback.TracebackException(*exc_info, limit=-1) 2209 exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True) 2210 self.assertEqual(exc4, exc5) 2211 2212 # there are locals in the next-to-innermost frame 2213 exc6 = traceback.TracebackException(*exc_info, limit=-2) 2214 exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True) 2215 self.assertNotEqual(exc6, exc7) 2216 2217 def test_comparison_equivalent_exceptions_are_equal(self): 2218 excs = [] 2219 for _ in range(2): 2220 try: 2221 1/0 2222 except: 2223 excs.append(traceback.TracebackException(*sys.exc_info())) 2224 self.assertEqual(excs[0], excs[1]) 2225 self.assertEqual(list(excs[0].format()), list(excs[1].format())) 2226 2227 def test_unhashable(self): 2228 class UnhashableException(Exception): 2229 def __eq__(self, other): 2230 return True 2231 2232 ex1 = UnhashableException('ex1') 2233 ex2 = UnhashableException('ex2') 2234 try: 2235 raise ex2 from ex1 2236 except UnhashableException: 2237 try: 2238 raise ex1 2239 except UnhashableException: 2240 exc_info = sys.exc_info() 2241 exc = traceback.TracebackException(*exc_info) 2242 formatted = list(exc.format()) 2243 self.assertIn('UnhashableException: ex2\n', formatted[2]) 2244 self.assertIn('UnhashableException: ex1\n', formatted[6]) 2245 2246 def test_limit(self): 2247 def recurse(n): 2248 if n: 2249 recurse(n-1) 2250 else: 2251 1/0 2252 try: 2253 recurse(10) 2254 except Exception: 2255 exc_info = sys.exc_info() 2256 exc = traceback.TracebackException(*exc_info, limit=5) 2257 expected_stack = traceback.StackSummary.extract( 2258 traceback.walk_tb(exc_info[2]), limit=5) 2259 self.assertEqual(expected_stack, exc.stack) 2260 2261 def test_lookup_lines(self): 2262 linecache.clearcache() 2263 e = Exception("uh oh") 2264 c = test_code('/foo.py', 'method') 2265 f = test_frame(c, None, None) 2266 tb = test_tb(f, 6, None, 0) 2267 exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) 2268 self.assertEqual(linecache.cache, {}) 2269 linecache.updatecache('/foo.py', globals()) 2270 self.assertEqual(exc.stack[0].line, "import sys") 2271 2272 def test_locals(self): 2273 linecache.updatecache('/foo.py', globals()) 2274 e = Exception("uh oh") 2275 c = test_code('/foo.py', 'method') 2276 f = test_frame(c, globals(), {'something': 1, 'other': 'string'}) 2277 tb = test_tb(f, 6, None, 0) 2278 exc = traceback.TracebackException( 2279 Exception, e, tb, capture_locals=True) 2280 self.assertEqual( 2281 exc.stack[0].locals, {'something': '1', 'other': "'string'"}) 2282 2283 def test_no_locals(self): 2284 linecache.updatecache('/foo.py', globals()) 2285 e = Exception("uh oh") 2286 c = test_code('/foo.py', 'method') 2287 f = test_frame(c, globals(), {'something': 1}) 2288 tb = test_tb(f, 6, None, 0) 2289 exc = traceback.TracebackException(Exception, e, tb) 2290 self.assertEqual(exc.stack[0].locals, None) 2291 2292 def test_traceback_header(self): 2293 # do not print a traceback header if exc_traceback is None 2294 # see issue #24695 2295 exc = traceback.TracebackException(Exception, Exception("haven"), None) 2296 self.assertEqual(list(exc.format()), ["Exception: haven\n"]) 2297 2298 @requires_debug_ranges() 2299 def test_print(self): 2300 def f(): 2301 x = 12 2302 try: 2303 x/0 2304 except Exception: 2305 return sys.exc_info() 2306 exc = traceback.TracebackException(*f(), capture_locals=True) 2307 output = StringIO() 2308 exc.print(file=output) 2309 self.assertEqual( 2310 output.getvalue().split('\n')[-5:], 2311 [' x/0', 2312 ' ~^~', 2313 ' x = 12', 2314 'ZeroDivisionError: division by zero', 2315 '']) 2316 2317 2318class TestTracebackException_ExceptionGroups(unittest.TestCase): 2319 def setUp(self): 2320 super().setUp() 2321 self.eg_info = self._get_exception_group() 2322 2323 def _get_exception_group(self): 2324 def f(): 2325 1/0 2326 2327 def g(v): 2328 raise ValueError(v) 2329 2330 self.lno_f = f.__code__.co_firstlineno 2331 self.lno_g = g.__code__.co_firstlineno 2332 2333 try: 2334 try: 2335 try: 2336 f() 2337 except Exception as e: 2338 exc1 = e 2339 try: 2340 g(42) 2341 except Exception as e: 2342 exc2 = e 2343 raise ExceptionGroup("eg1", [exc1, exc2]) 2344 except ExceptionGroup as e: 2345 exc3 = e 2346 try: 2347 g(24) 2348 except Exception as e: 2349 exc4 = e 2350 raise ExceptionGroup("eg2", [exc3, exc4]) 2351 except ExceptionGroup: 2352 return sys.exc_info() 2353 self.fail('Exception Not Raised') 2354 2355 def test_exception_group_construction(self): 2356 eg_info = self.eg_info 2357 teg1 = traceback.TracebackException(*eg_info) 2358 teg2 = traceback.TracebackException.from_exception(eg_info[1]) 2359 self.assertIsNot(teg1, teg2) 2360 self.assertEqual(teg1, teg2) 2361 2362 def test_exception_group_format_exception_only(self): 2363 teg = traceback.TracebackException(*self.eg_info) 2364 formatted = ''.join(teg.format_exception_only()).split('\n') 2365 expected = "ExceptionGroup: eg2\n".split('\n') 2366 2367 self.assertEqual(formatted, expected) 2368 2369 def test_exception_group_format(self): 2370 teg = traceback.TracebackException(*self.eg_info) 2371 2372 formatted = ''.join(teg.format()).split('\n') 2373 lno_f = self.lno_f 2374 lno_g = self.lno_g 2375 2376 expected = [ 2377 f' + Exception Group Traceback (most recent call last):', 2378 f' | File "{__file__}", line {lno_g+23}, in _get_exception_group', 2379 f' | raise ExceptionGroup("eg2", [exc3, exc4])', 2380 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', 2381 f' | ExceptionGroup: eg2', 2382 f' +-+---------------- 1 ----------------', 2383 f' | Exception Group Traceback (most recent call last):', 2384 f' | File "{__file__}", line {lno_g+16}, in _get_exception_group', 2385 f' | raise ExceptionGroup("eg1", [exc1, exc2])', 2386 f' | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^', 2387 f' | ExceptionGroup: eg1', 2388 f' +-+---------------- 1 ----------------', 2389 f' | Traceback (most recent call last):', 2390 f' | File "{__file__}", line {lno_g+9}, in _get_exception_group', 2391 f' | f()', 2392 f' | ^^^', 2393 f' | File "{__file__}", line {lno_f+1}, in f', 2394 f' | 1/0', 2395 f' | ~^~', 2396 f' | ZeroDivisionError: division by zero', 2397 f' +---------------- 2 ----------------', 2398 f' | Traceback (most recent call last):', 2399 f' | File "{__file__}", line {lno_g+13}, in _get_exception_group', 2400 f' | g(42)', 2401 f' | ^^^^^', 2402 f' | File "{__file__}", line {lno_g+1}, in g', 2403 f' | raise ValueError(v)', 2404 f' | ^^^^^^^^^^^^^^^^^^^', 2405 f' | ValueError: 42', 2406 f' +------------------------------------', 2407 f' +---------------- 2 ----------------', 2408 f' | Traceback (most recent call last):', 2409 f' | File "{__file__}", line {lno_g+20}, in _get_exception_group', 2410 f' | g(24)', 2411 f' | ^^^^^', 2412 f' | File "{__file__}", line {lno_g+1}, in g', 2413 f' | raise ValueError(v)', 2414 f' | ^^^^^^^^^^^^^^^^^^^', 2415 f' | ValueError: 24', 2416 f' +------------------------------------', 2417 f''] 2418 2419 self.assertEqual(formatted, expected) 2420 2421 def test_max_group_width(self): 2422 excs1 = [] 2423 excs2 = [] 2424 for i in range(3): 2425 excs1.append(ValueError(i)) 2426 for i in range(10): 2427 excs2.append(TypeError(i)) 2428 2429 EG = ExceptionGroup 2430 eg = EG('eg', [EG('eg1', excs1), EG('eg2', excs2)]) 2431 2432 teg = traceback.TracebackException.from_exception(eg, max_group_width=2) 2433 formatted = ''.join(teg.format()).split('\n') 2434 2435 expected = [ 2436 f' | ExceptionGroup: eg', 2437 f' +-+---------------- 1 ----------------', 2438 f' | ExceptionGroup: eg1', 2439 f' +-+---------------- 1 ----------------', 2440 f' | ValueError: 0', 2441 f' +---------------- 2 ----------------', 2442 f' | ValueError: 1', 2443 f' +---------------- ... ----------------', 2444 f' | and 1 more exception', 2445 f' +------------------------------------', 2446 f' +---------------- 2 ----------------', 2447 f' | ExceptionGroup: eg2', 2448 f' +-+---------------- 1 ----------------', 2449 f' | TypeError: 0', 2450 f' +---------------- 2 ----------------', 2451 f' | TypeError: 1', 2452 f' +---------------- ... ----------------', 2453 f' | and 8 more exceptions', 2454 f' +------------------------------------', 2455 f''] 2456 2457 self.assertEqual(formatted, expected) 2458 2459 def test_max_group_depth(self): 2460 exc = TypeError('bad type') 2461 for i in range(3): 2462 exc = ExceptionGroup('exc', [ValueError(-i), exc, ValueError(i)]) 2463 2464 teg = traceback.TracebackException.from_exception(exc, max_group_depth=2) 2465 formatted = ''.join(teg.format()).split('\n') 2466 2467 expected = [ 2468 f' | ExceptionGroup: exc', 2469 f' +-+---------------- 1 ----------------', 2470 f' | ValueError: -2', 2471 f' +---------------- 2 ----------------', 2472 f' | ExceptionGroup: exc', 2473 f' +-+---------------- 1 ----------------', 2474 f' | ValueError: -1', 2475 f' +---------------- 2 ----------------', 2476 f' | ... (max_group_depth is 2)', 2477 f' +---------------- 3 ----------------', 2478 f' | ValueError: 1', 2479 f' +------------------------------------', 2480 f' +---------------- 3 ----------------', 2481 f' | ValueError: 2', 2482 f' +------------------------------------', 2483 f''] 2484 2485 self.assertEqual(formatted, expected) 2486 2487 def test_comparison(self): 2488 try: 2489 raise self.eg_info[1] 2490 except ExceptionGroup: 2491 exc_info = sys.exc_info() 2492 for _ in range(5): 2493 try: 2494 raise exc_info[1] 2495 except: 2496 exc_info = sys.exc_info() 2497 exc = traceback.TracebackException(*exc_info) 2498 exc2 = traceback.TracebackException(*exc_info) 2499 exc3 = traceback.TracebackException(*exc_info, limit=300) 2500 ne = traceback.TracebackException(*exc_info, limit=3) 2501 self.assertIsNot(exc, exc2) 2502 self.assertEqual(exc, exc2) 2503 self.assertEqual(exc, exc3) 2504 self.assertNotEqual(exc, ne) 2505 self.assertNotEqual(exc, object()) 2506 self.assertEqual(exc, ALWAYS_EQ) 2507 2508 2509class MiscTest(unittest.TestCase): 2510 2511 def test_all(self): 2512 expected = set() 2513 denylist = {'print_list'} 2514 for name in dir(traceback): 2515 if name.startswith('_') or name in denylist: 2516 continue 2517 module_object = getattr(traceback, name) 2518 if getattr(module_object, '__module__', None) == 'traceback': 2519 expected.add(name) 2520 self.assertCountEqual(traceback.__all__, expected) 2521 2522 2523if __name__ == "__main__": 2524 unittest.main() 2525