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