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