python (3.11.7)
       1  """Test cases for traceback module"""
       2  
       3  from collections import namedtuple
       4  from io import StringIO
       5  import linecache
       6  import sys
       7  import types
       8  import inspect
       9  import unittest
      10  import re
      11  from test import support
      12  from test.support import (Error, captured_output, cpython_only, ALWAYS_EQ,
      13                            requires_debug_ranges, has_no_debug_ranges,
      14                            requires_subprocess)
      15  from test.support.os_helper import TESTFN, unlink
      16  from test.support.script_helper import assert_python_ok, assert_python_failure
      17  
      18  import os
      19  import textwrap
      20  import traceback
      21  from functools import partial
      22  
      23  MODULE_PREFIX = f'{__name__}.' if __name__ == '__main__' else ''
      24  
      25  test_code = namedtuple('code', ['co_filename', 'co_name'])
      26  test_code.co_positions = lambda _: iter([(6, 6, 0, 0)])
      27  test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals'])
      28  test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next', 'tb_lasti'])
      29  
      30  
      31  class ESC[4;38;5;81mTracebackCases(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      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 ESC[4;38;5;81mX(ESC[4;38;5;149mException):
     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()
     376  class ESC[4;38;5;81mTracebackErrorLocationCaretTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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_binary_operators_with_spaces_and_parenthesis(self):
     570          def f_with_binary_operator():
     571              a = 1
     572              b = ""
     573              return ( a   )   + b
     574  
     575          lineno_f = f_with_binary_operator.__code__.co_firstlineno
     576          expected_error = (
     577              'Traceback (most recent call last):\n'
     578              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     579              '    callable()\n'
     580              f'  File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
     581              '    return ( a   )   + b\n'
     582              '           ~~~~~~~~~~^~~\n'
     583          )
     584          result_lines = self.get_exception(f_with_binary_operator)
     585          self.assertEqual(result_lines, expected_error.splitlines())
     586  
     587      def test_caret_for_subscript(self):
     588          def f_with_subscript():
     589              some_dict = {'x': {'y': None}}
     590              return some_dict['x']['y']['z']
     591  
     592          lineno_f = f_with_subscript.__code__.co_firstlineno
     593          expected_error = (
     594              'Traceback (most recent call last):\n'
     595              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     596              '    callable()\n'
     597              f'  File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
     598              "    return some_dict['x']['y']['z']\n"
     599              '           ~~~~~~~~~~~~~~~~~~~^^^^^\n'
     600          )
     601          result_lines = self.get_exception(f_with_subscript)
     602          self.assertEqual(result_lines, expected_error.splitlines())
     603  
     604      def test_caret_for_subscript_unicode(self):
     605          def f_with_subscript():
     606              some_dict = {'ó': {'á': {'í': {'theta': 1}}}}
     607              return some_dict['ó']['á']['í']['beta']
     608  
     609          lineno_f = f_with_subscript.__code__.co_firstlineno
     610          expected_error = (
     611              'Traceback (most recent call last):\n'
     612              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     613              '    callable()\n'
     614              f'  File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
     615              "    return some_dict['ó']['á']['í']['beta']\n"
     616              '           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^\n'
     617          )
     618          result_lines = self.get_exception(f_with_subscript)
     619          self.assertEqual(result_lines, expected_error.splitlines())
     620  
     621      def test_caret_for_subscript_with_spaces_and_parenthesis(self):
     622          def f_with_binary_operator():
     623              a = []
     624              b = c = 1
     625              return b     [    a  ] + c
     626  
     627          lineno_f = f_with_binary_operator.__code__.co_firstlineno
     628          expected_error = (
     629              'Traceback (most recent call last):\n'
     630              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     631              '    callable()\n'
     632              f'  File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
     633              '    return b     [    a  ] + c\n'
     634              '           ~~~~~~^^^^^^^^^\n'
     635          )
     636          result_lines = self.get_exception(f_with_binary_operator)
     637          self.assertEqual(result_lines, expected_error.splitlines())
     638  
     639      def test_traceback_specialization_with_syntax_error(self):
     640          bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec")
     641  
     642          with open(TESTFN, "w") as file:
     643              # make the file's contents invalid
     644              file.write("1 $ 0 / 1 / 2\n")
     645          self.addCleanup(unlink, TESTFN)
     646  
     647          func = partial(exec, bytecode)
     648          result_lines = self.get_exception(func)
     649  
     650          lineno_f = bytecode.co_firstlineno
     651          expected_error = (
     652              'Traceback (most recent call last):\n'
     653              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     654              '    callable()\n'
     655              f'  File "{TESTFN}", line {lineno_f}, in <module>\n'
     656              "    1 $ 0 / 1 / 2\n"
     657              '    ^^^^^\n'
     658          )
     659          self.assertEqual(result_lines, expected_error.splitlines())
     660  
     661      def test_traceback_very_long_line(self):
     662          source = "if True: " + "a" * 256
     663          bytecode = compile(source, TESTFN, "exec")
     664  
     665          with open(TESTFN, "w") as file:
     666              file.write(source)
     667          self.addCleanup(unlink, TESTFN)
     668  
     669          func = partial(exec, bytecode)
     670          result_lines = self.get_exception(func)
     671  
     672          lineno_f = bytecode.co_firstlineno
     673          expected_error = (
     674              'Traceback (most recent call last):\n'
     675              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     676              '    callable()\n'
     677              f'  File "{TESTFN}", line {lineno_f}, in <module>\n'
     678              f'    {source}\n'
     679              f'    {" "*len("if True: ") + "^"*256}\n'
     680          )
     681          self.assertEqual(result_lines, expected_error.splitlines())
     682  
     683      def test_secondary_caret_not_elided(self):
     684          # Always show a line's indicators if they include the secondary character.
     685          def f_with_subscript():
     686              some_dict = {'x': {'y': None}}
     687              some_dict['x']['y']['z']
     688  
     689          lineno_f = f_with_subscript.__code__.co_firstlineno
     690          expected_error = (
     691              'Traceback (most recent call last):\n'
     692              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     693              '    callable()\n'
     694              f'  File "{__file__}", line {lineno_f+2}, in f_with_subscript\n'
     695              "    some_dict['x']['y']['z']\n"
     696              '    ~~~~~~~~~~~~~~~~~~~^^^^^\n'
     697          )
     698          result_lines = self.get_exception(f_with_subscript)
     699          self.assertEqual(result_lines, expected_error.splitlines())
     700  
     701      def test_caret_exception_group(self):
     702          # Notably, this covers whether indicators handle margin strings correctly.
     703          # (Exception groups use margin strings to display vertical indicators.)
     704          # The implementation must account for both "indent" and "margin" offsets.
     705  
     706          def exc():
     707              if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
     708  
     709          expected_error = (
     710               f'  + Exception Group Traceback (most recent call last):\n'
     711               f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
     712               f'  |     callable()\n'
     713               f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
     714               f'  |     if True: raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
     715               f'  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n'
     716               f'  | ExceptionGroup: eg (2 sub-exceptions)\n'
     717               f'  +-+---------------- 1 ----------------\n'
     718               f'    | ValueError: 1\n'
     719               f'    +---------------- 2 ----------------\n'
     720               f'    | TypeError: 2\n')
     721  
     722          result_lines = self.get_exception(exc)
     723          self.assertEqual(result_lines, expected_error.splitlines())
     724  
     725      def assertSpecialized(self, func, expected_specialization):
     726          result_lines = self.get_exception(func)
     727          specialization_line = result_lines[-1]
     728          self.assertEqual(specialization_line.lstrip(), expected_specialization)
     729  
     730      def test_specialization_variations(self):
     731          self.assertSpecialized(lambda: 1/0,
     732                                        "~^~")
     733          self.assertSpecialized(lambda: 1/0/3,
     734                                        "~^~")
     735          self.assertSpecialized(lambda: 1 / 0,
     736                                        "~~^~~")
     737          self.assertSpecialized(lambda: 1 / 0 / 3,
     738                                        "~~^~~")
     739          self.assertSpecialized(lambda: 1/ 0,
     740                                        "~^~~")
     741          self.assertSpecialized(lambda: 1/ 0/3,
     742                                        "~^~~")
     743          self.assertSpecialized(lambda: 1    /  0,
     744                                        "~~~~~^~~~")
     745          self.assertSpecialized(lambda: 1    /  0   / 5,
     746                                        "~~~~~^~~~")
     747          self.assertSpecialized(lambda: 1 /0,
     748                                        "~~^~")
     749          self.assertSpecialized(lambda: 1//0,
     750                                        "~^^~")
     751          self.assertSpecialized(lambda: 1//0//4,
     752                                        "~^^~")
     753          self.assertSpecialized(lambda: 1 // 0,
     754                                        "~~^^~~")
     755          self.assertSpecialized(lambda: 1 // 0 // 4,
     756                                        "~~^^~~")
     757          self.assertSpecialized(lambda: 1 //0,
     758                                        "~~^^~")
     759          self.assertSpecialized(lambda: 1// 0,
     760                                        "~^^~~")
     761  
     762      def test_decorator_application_lineno_correct(self):
     763          def dec_error(func):
     764              raise TypeError
     765          def dec_fine(func):
     766              return func
     767          def applydecs():
     768              @dec_error
     769              @dec_fine
     770              def g(): pass
     771          result_lines = self.get_exception(applydecs)
     772          lineno_applydescs = applydecs.__code__.co_firstlineno
     773          lineno_dec_error = dec_error.__code__.co_firstlineno
     774          expected_error = (
     775              'Traceback (most recent call last):\n'
     776              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     777              '    callable()\n'
     778              f'  File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n'
     779              '    @dec_error\n'
     780              '     ^^^^^^^^^\n'
     781              f'  File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
     782              '    raise TypeError\n'
     783          )
     784          self.assertEqual(result_lines, expected_error.splitlines())
     785  
     786          def applydecs_class():
     787              @dec_error
     788              @dec_fine
     789              class ESC[4;38;5;81mA: pass
     790          result_lines = self.get_exception(applydecs_class)
     791          lineno_applydescs_class = applydecs_class.__code__.co_firstlineno
     792          expected_error = (
     793              'Traceback (most recent call last):\n'
     794              f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
     795              '    callable()\n'
     796              f'  File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n'
     797              '    @dec_error\n'
     798              '     ^^^^^^^^^\n'
     799              f'  File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n'
     800              '    raise TypeError\n'
     801          )
     802          self.assertEqual(result_lines, expected_error.splitlines())
     803  
     804      def test_multiline_method_call_a(self):
     805          def f():
     806              (None
     807                  .method
     808              )()
     809          actual = self.get_exception(f)
     810          expected = [
     811              f"Traceback (most recent call last):",
     812              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     813              f"    callable()",
     814              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
     815              f"    .method",
     816              f"     ^^^^^^",
     817          ]
     818          self.assertEqual(actual, expected)
     819  
     820      def test_multiline_method_call_b(self):
     821          def f():
     822              (None.
     823                  method
     824              )()
     825          actual = self.get_exception(f)
     826          expected = [
     827              f"Traceback (most recent call last):",
     828              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     829              f"    callable()",
     830              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
     831              f"    method",
     832          ]
     833          self.assertEqual(actual, expected)
     834  
     835      def test_multiline_method_call_c(self):
     836          def f():
     837              (None
     838                  . method
     839              )()
     840          actual = self.get_exception(f)
     841          expected = [
     842              f"Traceback (most recent call last):",
     843              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     844              f"    callable()",
     845              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
     846              f"    . method",
     847              f"      ^^^^^^",
     848          ]
     849          self.assertEqual(actual, expected)
     850  
     851      def test_wide_characters_unicode_with_problematic_byte_offset(self):
     852          def f():
     853              width
     854  
     855          actual = self.get_exception(f)
     856          expected = [
     857              f"Traceback (most recent call last):",
     858              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     859              f"    callable()",
     860              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
     861              f"    width",
     862          ]
     863          self.assertEqual(actual, expected)
     864  
     865  
     866      def test_byte_offset_with_wide_characters_middle(self):
     867          def f():
     868              width = 1
     869              raise ValueError(width)
     870  
     871          actual = self.get_exception(f)
     872          expected = [
     873              f"Traceback (most recent call last):",
     874              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     875              f"    callable()",
     876              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 2}, in f",
     877              f"    raise ValueError(width)",
     878          ]
     879          self.assertEqual(actual, expected)
     880  
     881      def test_byte_offset_multiline(self):
     882          def f():
     883              www = 1
     884              th = 0
     885  
     886              print(1, www(
     887                      th))
     888  
     889          actual = self.get_exception(f)
     890          expected = [
     891              f"Traceback (most recent call last):",
     892              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     893              f"    callable()",
     894              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 4}, in f",
     895              f"    print(1, www(",
     896              f"             ^^^^^^^",
     897          ]
     898          self.assertEqual(actual, expected)
     899  
     900      def test_byte_offset_with_wide_characters_term_highlight(self):
     901          def f():
     902              说明说明 = 1
     903              şçöğıĤellö = 0 # not wide but still non-ascii
     904              return 说明说明 / şçöğıĤellö
     905  
     906          actual = self.get_exception(f)
     907          expected = [
     908              f"Traceback (most recent call last):",
     909              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     910              f"    callable()",
     911              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 3}, in f",
     912              f"    return 说明说明 / şçöğıĤellö",
     913              f"           ~~~~~~~~~^~~~~~~~~~~~",
     914          ]
     915          self.assertEqual(actual, expected)
     916  
     917      def test_byte_offset_with_emojis_term_highlight(self):
     918          def f():
     919              return "✨🐍" + func_说明说明("📗🚛",
     920                  "📗🚛") + "🐍"
     921  
     922          actual = self.get_exception(f)
     923          expected = [
     924              f"Traceback (most recent call last):",
     925              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     926              f"    callable()",
     927              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 1}, in f",
     928              f'    return "✨🐍" + func_说明说明("📗🚛",',
     929              f"                    ^^^^^^^^^^^^^",
     930          ]
     931          self.assertEqual(actual, expected)
     932  
     933      def test_byte_offset_wide_chars_subscript(self):
     934          def f():
     935              my_dct = {
     936                  "✨🚛✨": {
     937                      "说明": {
     938                          "🐍🐍🐍": None
     939                      }
     940                  }
     941              }
     942              return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"]
     943  
     944          actual = self.get_exception(f)
     945          expected = [
     946              f"Traceback (most recent call last):",
     947              f"  File \"{__file__}\", line {self.callable_line}, in get_exception",
     948              f"    callable()",
     949              f"  File \"{__file__}\", line {f.__code__.co_firstlineno + 8}, in f",
     950              f'    return my_dct["✨🚛✨"]["说明"]["🐍"]["说明"]["🐍🐍"]',
     951              f"           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^",
     952          ]
     953          self.assertEqual(actual, expected)
     954  
     955  
     956  @cpython_only
     957  @requires_debug_ranges()
     958  class ESC[4;38;5;81mCPythonTracebackErrorCaretTests(ESC[4;38;5;149mTracebackErrorLocationCaretTests):
     959      """
     960      Same set of tests as above but with Python's internal traceback printing.
     961      """
     962      def get_exception(self, callable):
     963          from _testcapi import exception_print
     964          try:
     965              callable()
     966              self.fail("No exception thrown.")
     967          except Exception as e:
     968              with captured_output("stderr") as tbstderr:
     969                  exception_print(e)
     970              return tbstderr.getvalue().splitlines()[:-1]
     971  
     972      callable_line = get_exception.__code__.co_firstlineno + 3
     973  
     974  
     975  class ESC[4;38;5;81mTracebackFormatTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     976  
     977      def some_exception(self):
     978          raise KeyError('blah')
     979  
     980      @cpython_only
     981      def check_traceback_format(self, cleanup_func=None):
     982          from _testcapi import traceback_print
     983          try:
     984              self.some_exception()
     985          except KeyError:
     986              type_, value, tb = sys.exc_info()
     987              if cleanup_func is not None:
     988                  # Clear the inner frames, not this one
     989                  cleanup_func(tb.tb_next)
     990              traceback_fmt = 'Traceback (most recent call last):\n' + \
     991                              ''.join(traceback.format_tb(tb))
     992              file_ = StringIO()
     993              traceback_print(tb, file_)
     994              python_fmt  = file_.getvalue()
     995              # Call all _tb and _exc functions
     996              with captured_output("stderr") as tbstderr:
     997                  traceback.print_tb(tb)
     998              tbfile = StringIO()
     999              traceback.print_tb(tb, file=tbfile)
    1000              with captured_output("stderr") as excstderr:
    1001                  traceback.print_exc()
    1002              excfmt = traceback.format_exc()
    1003              excfile = StringIO()
    1004              traceback.print_exc(file=excfile)
    1005          else:
    1006              raise Error("unable to create test traceback string")
    1007  
    1008          # Make sure that Python and the traceback module format the same thing
    1009          self.assertEqual(traceback_fmt, python_fmt)
    1010          # Now verify the _tb func output
    1011          self.assertEqual(tbstderr.getvalue(), tbfile.getvalue())
    1012          # Now verify the _exc func output
    1013          self.assertEqual(excstderr.getvalue(), excfile.getvalue())
    1014          self.assertEqual(excfmt, excfile.getvalue())
    1015  
    1016          # Make sure that the traceback is properly indented.
    1017          tb_lines = python_fmt.splitlines()
    1018          banner = tb_lines[0]
    1019          self.assertEqual(len(tb_lines), 5)
    1020          location, source_line = tb_lines[-2], tb_lines[-1]
    1021          self.assertTrue(banner.startswith('Traceback'))
    1022          self.assertTrue(location.startswith('  File'))
    1023          self.assertTrue(source_line.startswith('    raise'))
    1024  
    1025      def test_traceback_format(self):
    1026          self.check_traceback_format()
    1027  
    1028      def test_traceback_format_with_cleared_frames(self):
    1029          # Check that traceback formatting also works with a clear()ed frame
    1030          def cleanup_tb(tb):
    1031              tb.tb_frame.clear()
    1032          self.check_traceback_format(cleanup_tb)
    1033  
    1034      def test_stack_format(self):
    1035          # Verify _stack functions. Note we have to use _getframe(1) to
    1036          # compare them without this frame appearing in the output
    1037          with captured_output("stderr") as ststderr:
    1038              traceback.print_stack(sys._getframe(1))
    1039          stfile = StringIO()
    1040          traceback.print_stack(sys._getframe(1), file=stfile)
    1041          self.assertEqual(ststderr.getvalue(), stfile.getvalue())
    1042  
    1043          stfmt = traceback.format_stack(sys._getframe(1))
    1044  
    1045          self.assertEqual(ststderr.getvalue(), "".join(stfmt))
    1046  
    1047      def test_print_stack(self):
    1048          def prn():
    1049              traceback.print_stack()
    1050          with captured_output("stderr") as stderr:
    1051              prn()
    1052          lineno = prn.__code__.co_firstlineno
    1053          self.assertEqual(stderr.getvalue().splitlines()[-4:], [
    1054              '  File "%s", line %d, in test_print_stack' % (__file__, lineno+3),
    1055              '    prn()',
    1056              '  File "%s", line %d, in prn' % (__file__, lineno+1),
    1057              '    traceback.print_stack()',
    1058          ])
    1059  
    1060      # issue 26823 - Shrink recursive tracebacks
    1061      def _check_recursive_traceback_display(self, render_exc):
    1062          # Always show full diffs when this test fails
    1063          # Note that rearranging things may require adjusting
    1064          # the relative line numbers in the expected tracebacks
    1065          self.maxDiff = None
    1066  
    1067          # Check hitting the recursion limit
    1068          def f():
    1069              f()
    1070  
    1071          with captured_output("stderr") as stderr_f:
    1072              try:
    1073                  f()
    1074              except RecursionError:
    1075                  render_exc()
    1076              else:
    1077                  self.fail("no recursion occurred")
    1078  
    1079          lineno_f = f.__code__.co_firstlineno
    1080          result_f = (
    1081              'Traceback (most recent call last):\n'
    1082              f'  File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n'
    1083              '    f()\n'
    1084              f'  File "{__file__}", line {lineno_f+1}, in f\n'
    1085              '    f()\n'
    1086              f'  File "{__file__}", line {lineno_f+1}, in f\n'
    1087              '    f()\n'
    1088              f'  File "{__file__}", line {lineno_f+1}, in f\n'
    1089              '    f()\n'
    1090              # XXX: The following line changes depending on whether the tests
    1091              # are run through the interactive interpreter or with -m
    1092              # It also varies depending on the platform (stack size)
    1093              # Fortunately, we don't care about exactness here, so we use regex
    1094              r'  \[Previous line repeated (\d+) more times\]' '\n'
    1095              'RecursionError: maximum recursion depth exceeded\n'
    1096          )
    1097  
    1098          expected = result_f.splitlines()
    1099          actual = stderr_f.getvalue().splitlines()
    1100  
    1101          # Check the output text matches expectations
    1102          # 2nd last line contains the repetition count
    1103          self.assertEqual(actual[:-2], expected[:-2])
    1104          self.assertRegex(actual[-2], expected[-2])
    1105          # last line can have additional text appended
    1106          self.assertIn(expected[-1], actual[-1])
    1107  
    1108          # Check the recursion count is roughly as expected
    1109          rec_limit = sys.getrecursionlimit()
    1110          self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
    1111  
    1112          # Check a known (limited) number of recursive invocations
    1113          def g(count=10):
    1114              if count:
    1115                  return g(count-1)
    1116              raise ValueError
    1117  
    1118          with captured_output("stderr") as stderr_g:
    1119              try:
    1120                  g()
    1121              except ValueError:
    1122                  render_exc()
    1123              else:
    1124                  self.fail("no value error was raised")
    1125  
    1126          lineno_g = g.__code__.co_firstlineno
    1127          result_g = (
    1128              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1129              '    return g(count-1)\n'
    1130              '           ^^^^^^^^^^\n'
    1131              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1132              '    return g(count-1)\n'
    1133              '           ^^^^^^^^^^\n'
    1134              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1135              '    return g(count-1)\n'
    1136              '           ^^^^^^^^^^\n'
    1137              '  [Previous line repeated 7 more times]\n'
    1138              f'  File "{__file__}", line {lineno_g+3}, in g\n'
    1139              '    raise ValueError\n'
    1140              'ValueError\n'
    1141          )
    1142          tb_line = (
    1143              'Traceback (most recent call last):\n'
    1144              f'  File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
    1145              '    g()\n'
    1146          )
    1147          expected = (tb_line + result_g).splitlines()
    1148          actual = stderr_g.getvalue().splitlines()
    1149          self.assertEqual(actual, expected)
    1150  
    1151          # Check 2 different repetitive sections
    1152          def h(count=10):
    1153              if count:
    1154                  return h(count-1)
    1155              g()
    1156  
    1157          with captured_output("stderr") as stderr_h:
    1158              try:
    1159                  h()
    1160              except ValueError:
    1161                  render_exc()
    1162              else:
    1163                  self.fail("no value error was raised")
    1164  
    1165          lineno_h = h.__code__.co_firstlineno
    1166          result_h = (
    1167              'Traceback (most recent call last):\n'
    1168              f'  File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
    1169              '    h()\n'
    1170              f'  File "{__file__}", line {lineno_h+2}, in h\n'
    1171              '    return h(count-1)\n'
    1172              '           ^^^^^^^^^^\n'
    1173              f'  File "{__file__}", line {lineno_h+2}, in h\n'
    1174              '    return h(count-1)\n'
    1175              '           ^^^^^^^^^^\n'
    1176              f'  File "{__file__}", line {lineno_h+2}, in h\n'
    1177              '    return h(count-1)\n'
    1178              '           ^^^^^^^^^^\n'
    1179              '  [Previous line repeated 7 more times]\n'
    1180              f'  File "{__file__}", line {lineno_h+3}, in h\n'
    1181              '    g()\n'
    1182          )
    1183          expected = (result_h + result_g).splitlines()
    1184          actual = stderr_h.getvalue().splitlines()
    1185          self.assertEqual(actual, expected)
    1186  
    1187          # Check the boundary conditions. First, test just below the cutoff.
    1188          with captured_output("stderr") as stderr_g:
    1189              try:
    1190                  g(traceback._RECURSIVE_CUTOFF)
    1191              except ValueError:
    1192                  render_exc()
    1193              else:
    1194                  self.fail("no error raised")
    1195          result_g = (
    1196              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1197              '    return g(count-1)\n'
    1198              '           ^^^^^^^^^^\n'
    1199              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1200              '    return g(count-1)\n'
    1201              '           ^^^^^^^^^^\n'
    1202              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1203              '    return g(count-1)\n'
    1204              '           ^^^^^^^^^^\n'
    1205              f'  File "{__file__}", line {lineno_g+3}, in g\n'
    1206              '    raise ValueError\n'
    1207              'ValueError\n'
    1208          )
    1209          tb_line = (
    1210              'Traceback (most recent call last):\n'
    1211              f'  File "{__file__}", line {lineno_g+77}, in _check_recursive_traceback_display\n'
    1212              '    g(traceback._RECURSIVE_CUTOFF)\n'
    1213          )
    1214          expected = (tb_line + result_g).splitlines()
    1215          actual = stderr_g.getvalue().splitlines()
    1216          self.assertEqual(actual, expected)
    1217  
    1218          # Second, test just above the cutoff.
    1219          with captured_output("stderr") as stderr_g:
    1220              try:
    1221                  g(traceback._RECURSIVE_CUTOFF + 1)
    1222              except ValueError:
    1223                  render_exc()
    1224              else:
    1225                  self.fail("no error raised")
    1226          result_g = (
    1227              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1228              '    return g(count-1)\n'
    1229              '           ^^^^^^^^^^\n'
    1230              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1231              '    return g(count-1)\n'
    1232              '           ^^^^^^^^^^\n'
    1233              f'  File "{__file__}", line {lineno_g+2}, in g\n'
    1234              '    return g(count-1)\n'
    1235              '           ^^^^^^^^^^\n'
    1236              '  [Previous line repeated 1 more time]\n'
    1237              f'  File "{__file__}", line {lineno_g+3}, in g\n'
    1238              '    raise ValueError\n'
    1239              'ValueError\n'
    1240          )
    1241          tb_line = (
    1242              'Traceback (most recent call last):\n'
    1243              f'  File "{__file__}", line {lineno_g+108}, in _check_recursive_traceback_display\n'
    1244              '    g(traceback._RECURSIVE_CUTOFF + 1)\n'
    1245          )
    1246          expected = (tb_line + result_g).splitlines()
    1247          actual = stderr_g.getvalue().splitlines()
    1248          self.assertEqual(actual, expected)
    1249  
    1250      @requires_debug_ranges()
    1251      def test_recursive_traceback_python(self):
    1252          self._check_recursive_traceback_display(traceback.print_exc)
    1253  
    1254      @cpython_only
    1255      @requires_debug_ranges()
    1256      def test_recursive_traceback_cpython_internal(self):
    1257          from _testcapi import exception_print
    1258          def render_exc():
    1259              exc_type, exc_value, exc_tb = sys.exc_info()
    1260              exception_print(exc_value)
    1261          self._check_recursive_traceback_display(render_exc)
    1262  
    1263      def test_format_stack(self):
    1264          def fmt():
    1265              return traceback.format_stack()
    1266          result = fmt()
    1267          lineno = fmt.__code__.co_firstlineno
    1268          self.assertEqual(result[-2:], [
    1269              '  File "%s", line %d, in test_format_stack\n'
    1270              '    result = fmt()\n' % (__file__, lineno+2),
    1271              '  File "%s", line %d, in fmt\n'
    1272              '    return traceback.format_stack()\n' % (__file__, lineno+1),
    1273          ])
    1274  
    1275      @cpython_only
    1276      def test_unhashable(self):
    1277          from _testcapi import exception_print
    1278  
    1279          class ESC[4;38;5;81mUnhashableException(ESC[4;38;5;149mException):
    1280              def __eq__(self, other):
    1281                  return True
    1282  
    1283          ex1 = UnhashableException('ex1')
    1284          ex2 = UnhashableException('ex2')
    1285          try:
    1286              raise ex2 from ex1
    1287          except UnhashableException:
    1288              try:
    1289                  raise ex1
    1290              except UnhashableException:
    1291                  exc_type, exc_val, exc_tb = sys.exc_info()
    1292  
    1293          with captured_output("stderr") as stderr_f:
    1294              exception_print(exc_val)
    1295  
    1296          tb = stderr_f.getvalue().strip().splitlines()
    1297          self.assertEqual(11, len(tb))
    1298          self.assertEqual(context_message.strip(), tb[5])
    1299          self.assertIn('UnhashableException: ex2', tb[3])
    1300          self.assertIn('UnhashableException: ex1', tb[10])
    1301  
    1302      def deep_eg(self):
    1303          e = TypeError(1)
    1304          for i in range(2000):
    1305              e = ExceptionGroup('eg', [e])
    1306          return e
    1307  
    1308      @cpython_only
    1309      def test_exception_group_deep_recursion_capi(self):
    1310          from _testcapi import exception_print
    1311          LIMIT = 75
    1312          eg = self.deep_eg()
    1313          with captured_output("stderr") as stderr_f:
    1314              with support.infinite_recursion(max_depth=LIMIT):
    1315                  exception_print(eg)
    1316          output = stderr_f.getvalue()
    1317          self.assertIn('ExceptionGroup', output)
    1318          self.assertLessEqual(output.count('ExceptionGroup'), LIMIT)
    1319  
    1320      def test_exception_group_deep_recursion_traceback(self):
    1321          LIMIT = 75
    1322          eg = self.deep_eg()
    1323          with captured_output("stderr") as stderr_f:
    1324              with support.infinite_recursion(max_depth=LIMIT):
    1325                  traceback.print_exception(type(eg), eg, eg.__traceback__)
    1326          output = stderr_f.getvalue()
    1327          self.assertIn('ExceptionGroup', output)
    1328          self.assertLessEqual(output.count('ExceptionGroup'), LIMIT)
    1329  
    1330      @cpython_only
    1331      def test_print_exception_bad_type_capi(self):
    1332          from _testcapi import exception_print
    1333          with captured_output("stderr") as stderr:
    1334              exception_print(42)
    1335          self.assertEqual(
    1336              stderr.getvalue(),
    1337              ('TypeError: print_exception(): '
    1338               'Exception expected for value, int found\n')
    1339          )
    1340  
    1341      def test_print_exception_bad_type_python(self):
    1342          msg = "Exception expected for value, int found"
    1343          with self.assertRaisesRegex(TypeError, msg):
    1344              traceback.print_exception(42)
    1345  
    1346  
    1347  cause_message = (
    1348      "\nThe above exception was the direct cause "
    1349      "of the following exception:\n\n")
    1350  
    1351  context_message = (
    1352      "\nDuring handling of the above exception, "
    1353      "another exception occurred:\n\n")
    1354  
    1355  boundaries = re.compile(
    1356      '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
    1357  
    1358  class ESC[4;38;5;81mBaseExceptionReportingTests:
    1359  
    1360      def get_exception(self, exception_or_callable):
    1361          if isinstance(exception_or_callable, BaseException):
    1362              return exception_or_callable
    1363          try:
    1364              exception_or_callable()
    1365          except Exception as e:
    1366              return e
    1367  
    1368      callable_line = get_exception.__code__.co_firstlineno + 4
    1369  
    1370      def zero_div(self):
    1371          1/0 # In zero_div
    1372  
    1373      def check_zero_div(self, msg):
    1374          lines = msg.splitlines()
    1375          if has_no_debug_ranges():
    1376              self.assertTrue(lines[-3].startswith('  File'))
    1377              self.assertIn('1/0 # In zero_div', lines[-2])
    1378          else:
    1379              self.assertTrue(lines[-4].startswith('  File'))
    1380              self.assertIn('1/0 # In zero_div', lines[-3])
    1381          self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
    1382  
    1383      def test_simple(self):
    1384          try:
    1385              1/0 # Marker
    1386          except ZeroDivisionError as _:
    1387              e = _
    1388          lines = self.get_report(e).splitlines()
    1389          if has_no_debug_ranges():
    1390              self.assertEqual(len(lines), 4)
    1391              self.assertTrue(lines[3].startswith('ZeroDivisionError'))
    1392          else:
    1393              self.assertEqual(len(lines), 5)
    1394              self.assertTrue(lines[4].startswith('ZeroDivisionError'))
    1395          self.assertTrue(lines[0].startswith('Traceback'))
    1396          self.assertTrue(lines[1].startswith('  File'))
    1397          self.assertIn('1/0 # Marker', lines[2])
    1398  
    1399      def test_cause(self):
    1400          def inner_raise():
    1401              try:
    1402                  self.zero_div()
    1403              except ZeroDivisionError as e:
    1404                  raise KeyError from e
    1405          def outer_raise():
    1406              inner_raise() # Marker
    1407          blocks = boundaries.split(self.get_report(outer_raise))
    1408          self.assertEqual(len(blocks), 3)
    1409          self.assertEqual(blocks[1], cause_message)
    1410          self.check_zero_div(blocks[0])
    1411          self.assertIn('inner_raise() # Marker', blocks[2])
    1412  
    1413      def test_context(self):
    1414          def inner_raise():
    1415              try:
    1416                  self.zero_div()
    1417              except ZeroDivisionError:
    1418                  raise KeyError
    1419          def outer_raise():
    1420              inner_raise() # Marker
    1421          blocks = boundaries.split(self.get_report(outer_raise))
    1422          self.assertEqual(len(blocks), 3)
    1423          self.assertEqual(blocks[1], context_message)
    1424          self.check_zero_div(blocks[0])
    1425          self.assertIn('inner_raise() # Marker', blocks[2])
    1426  
    1427      def test_context_suppression(self):
    1428          try:
    1429              try:
    1430                  raise Exception
    1431              except:
    1432                  raise ZeroDivisionError from None
    1433          except ZeroDivisionError as _:
    1434              e = _
    1435          lines = self.get_report(e).splitlines()
    1436          self.assertEqual(len(lines), 4)
    1437          self.assertTrue(lines[3].startswith('ZeroDivisionError'))
    1438          self.assertTrue(lines[0].startswith('Traceback'))
    1439          self.assertTrue(lines[1].startswith('  File'))
    1440          self.assertIn('ZeroDivisionError from None', lines[2])
    1441  
    1442      def test_cause_and_context(self):
    1443          # When both a cause and a context are set, only the cause should be
    1444          # displayed and the context should be muted.
    1445          def inner_raise():
    1446              try:
    1447                  self.zero_div()
    1448              except ZeroDivisionError as _e:
    1449                  e = _e
    1450              try:
    1451                  xyzzy
    1452              except NameError:
    1453                  raise KeyError from e
    1454          def outer_raise():
    1455              inner_raise() # Marker
    1456          blocks = boundaries.split(self.get_report(outer_raise))
    1457          self.assertEqual(len(blocks), 3)
    1458          self.assertEqual(blocks[1], cause_message)
    1459          self.check_zero_div(blocks[0])
    1460          self.assertIn('inner_raise() # Marker', blocks[2])
    1461  
    1462      def test_cause_recursive(self):
    1463          def inner_raise():
    1464              try:
    1465                  try:
    1466                      self.zero_div()
    1467                  except ZeroDivisionError as e:
    1468                      z = e
    1469                      raise KeyError from e
    1470              except KeyError as e:
    1471                  raise z from e
    1472          def outer_raise():
    1473              inner_raise() # Marker
    1474          blocks = boundaries.split(self.get_report(outer_raise))
    1475          self.assertEqual(len(blocks), 3)
    1476          self.assertEqual(blocks[1], cause_message)
    1477          # The first block is the KeyError raised from the ZeroDivisionError
    1478          self.assertIn('raise KeyError from e', blocks[0])
    1479          self.assertNotIn('1/0', blocks[0])
    1480          # The second block (apart from the boundary) is the ZeroDivisionError
    1481          # re-raised from the KeyError
    1482          self.assertIn('inner_raise() # Marker', blocks[2])
    1483          self.check_zero_div(blocks[2])
    1484  
    1485      def test_syntax_error_offset_at_eol(self):
    1486          # See #10186.
    1487          def e():
    1488              raise SyntaxError('', ('', 0, 5, 'hello'))
    1489          msg = self.get_report(e).splitlines()
    1490          self.assertEqual(msg[-2], "        ^")
    1491          def e():
    1492              exec("x = 5 | 4 |")
    1493          msg = self.get_report(e).splitlines()
    1494          self.assertEqual(msg[-2], '               ^')
    1495  
    1496      def test_syntax_error_no_lineno(self):
    1497          # See #34463.
    1498  
    1499          # Without filename
    1500          e = SyntaxError('bad syntax')
    1501          msg = self.get_report(e).splitlines()
    1502          self.assertEqual(msg,
    1503              ['SyntaxError: bad syntax'])
    1504          e.lineno = 100
    1505          msg = self.get_report(e).splitlines()
    1506          self.assertEqual(msg,
    1507              ['  File "<string>", line 100', 'SyntaxError: bad syntax'])
    1508  
    1509          # With filename
    1510          e = SyntaxError('bad syntax')
    1511          e.filename = 'myfile.py'
    1512  
    1513          msg = self.get_report(e).splitlines()
    1514          self.assertEqual(msg,
    1515              ['SyntaxError: bad syntax (myfile.py)'])
    1516          e.lineno = 100
    1517          msg = self.get_report(e).splitlines()
    1518          self.assertEqual(msg,
    1519              ['  File "myfile.py", line 100', 'SyntaxError: bad syntax'])
    1520  
    1521      def test_message_none(self):
    1522          # A message that looks like "None" should not be treated specially
    1523          err = self.get_report(Exception(None))
    1524          self.assertIn('Exception: None\n', err)
    1525          err = self.get_report(Exception('None'))
    1526          self.assertIn('Exception: None\n', err)
    1527          err = self.get_report(Exception())
    1528          self.assertIn('Exception\n', err)
    1529          err = self.get_report(Exception(''))
    1530          self.assertIn('Exception\n', err)
    1531  
    1532      def test_syntax_error_various_offsets(self):
    1533          for offset in range(-5, 10):
    1534              for add in [0, 2]:
    1535                  text = " "*add + "text%d" % offset
    1536                  expected = ['  File "file.py", line 1']
    1537                  if offset < 1:
    1538                      expected.append("    %s" % text.lstrip())
    1539                  elif offset <= 6:
    1540                      expected.append("    %s" % text.lstrip())
    1541                      expected.append("    %s^" % (" "*(offset-1)))
    1542                  else:
    1543                      expected.append("    %s" % text.lstrip())
    1544                      expected.append("    %s^" % (" "*5))
    1545                  expected.append("SyntaxError: msg")
    1546                  expected.append("")
    1547                  err = self.get_report(SyntaxError("msg", ("file.py", 1, offset+add, text)))
    1548                  exp = "\n".join(expected)
    1549                  self.assertEqual(exp, err)
    1550  
    1551      def test_exception_with_note(self):
    1552          e = ValueError(123)
    1553          vanilla = self.get_report(e)
    1554  
    1555          e.add_note('My Note')
    1556          self.assertEqual(self.get_report(e), vanilla + 'My Note\n')
    1557  
    1558          del e.__notes__
    1559          e.add_note('')
    1560          self.assertEqual(self.get_report(e), vanilla + '\n')
    1561  
    1562          del e.__notes__
    1563          e.add_note('Your Note')
    1564          self.assertEqual(self.get_report(e), vanilla + 'Your Note\n')
    1565  
    1566          del e.__notes__
    1567          self.assertEqual(self.get_report(e), vanilla)
    1568  
    1569      def test_exception_with_invalid_notes(self):
    1570          e = ValueError(123)
    1571          vanilla = self.get_report(e)
    1572  
    1573          # non-sequence __notes__
    1574          class ESC[4;38;5;81mBadThing:
    1575              def __str__(self):
    1576                  return 'bad str'
    1577  
    1578              def __repr__(self):
    1579                  return 'bad repr'
    1580  
    1581          # unprintable, non-sequence __notes__
    1582          class ESC[4;38;5;81mUnprintable:
    1583              def __repr__(self):
    1584                  raise ValueError('bad value')
    1585  
    1586          e.__notes__ = BadThing()
    1587          notes_repr = 'bad repr'
    1588          self.assertEqual(self.get_report(e), vanilla + notes_repr)
    1589  
    1590          e.__notes__ = Unprintable()
    1591          err_msg = '<__notes__ repr() failed>'
    1592          self.assertEqual(self.get_report(e), vanilla + err_msg)
    1593  
    1594          # non-string item in the __notes__ sequence
    1595          e.__notes__  = [BadThing(), 'Final Note']
    1596          bad_note = 'bad str'
    1597          self.assertEqual(self.get_report(e), vanilla + bad_note + '\nFinal Note\n')
    1598  
    1599          # unprintable, non-string item in the __notes__ sequence
    1600          e.__notes__  = [Unprintable(), 'Final Note']
    1601          err_msg = '<note str() failed>'
    1602          self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')
    1603  
    1604      def test_exception_with_multiple_notes(self):
    1605          for e in [ValueError(42), SyntaxError('bad syntax')]:
    1606              with self.subTest(e=e):
    1607                  vanilla = self.get_report(e)
    1608  
    1609                  e.add_note('Note 1')
    1610                  e.add_note('Note 2')
    1611                  e.add_note('Note 3')
    1612  
    1613                  self.assertEqual(
    1614                      self.get_report(e),
    1615                      vanilla + 'Note 1\n' + 'Note 2\n' + 'Note 3\n')
    1616  
    1617                  del e.__notes__
    1618                  e.add_note('Note 4')
    1619                  del e.__notes__
    1620                  e.add_note('Note 5')
    1621                  e.add_note('Note 6')
    1622  
    1623                  self.assertEqual(
    1624                      self.get_report(e),
    1625                      vanilla + 'Note 5\n' + 'Note 6\n')
    1626  
    1627      def test_exception_qualname(self):
    1628          class ESC[4;38;5;81mA:
    1629              class ESC[4;38;5;81mB:
    1630                  class ESC[4;38;5;81mX(ESC[4;38;5;149mException):
    1631                      def __str__(self):
    1632                          return "I am X"
    1633  
    1634          err = self.get_report(A.B.X())
    1635          str_value = 'I am X'
    1636          str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__])
    1637          exp = "%s: %s\n" % (str_name, str_value)
    1638          self.assertEqual(exp, MODULE_PREFIX + err)
    1639  
    1640      def test_exception_modulename(self):
    1641          class ESC[4;38;5;81mX(ESC[4;38;5;149mException):
    1642              def __str__(self):
    1643                  return "I am X"
    1644  
    1645          for modulename in '__main__', 'builtins', 'some_module':
    1646              X.__module__ = modulename
    1647              with self.subTest(modulename=modulename):
    1648                  err = self.get_report(X())
    1649                  str_value = 'I am X'
    1650                  if modulename in ['builtins', '__main__']:
    1651                      str_name = X.__qualname__
    1652                  else:
    1653                      str_name = '.'.join([X.__module__, X.__qualname__])
    1654                  exp = "%s: %s\n" % (str_name, str_value)
    1655                  self.assertEqual(exp, err)
    1656  
    1657      def test_exception_modulename_not_unicode(self):
    1658          class ESC[4;38;5;81mX(ESC[4;38;5;149mException):
    1659              def __str__(self):
    1660                  return "I am X"
    1661  
    1662          X.__module__ = 42
    1663  
    1664          err = self.get_report(X())
    1665          exp = f'<unknown>.{X.__qualname__}: I am X\n'
    1666          self.assertEqual(exp, err)
    1667  
    1668      def test_exception_bad__str__(self):
    1669          class ESC[4;38;5;81mX(ESC[4;38;5;149mException):
    1670              def __str__(self):
    1671                  1/0
    1672          err = self.get_report(X())
    1673          str_value = '<exception str() failed>'
    1674          str_name = '.'.join([X.__module__, X.__qualname__])
    1675          self.assertEqual(MODULE_PREFIX + err, f"{str_name}: {str_value}\n")
    1676  
    1677  
    1678      # #### Exception Groups ####
    1679  
    1680      def test_exception_group_basic(self):
    1681          def exc():
    1682              raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])
    1683  
    1684          expected = (
    1685               f'  + Exception Group Traceback (most recent call last):\n'
    1686               f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
    1687               f'  |     exception_or_callable()\n'
    1688               f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 1}, in exc\n'
    1689               f'  |     raise ExceptionGroup("eg", [ValueError(1), TypeError(2)])\n'
    1690               f'  | ExceptionGroup: eg (2 sub-exceptions)\n'
    1691               f'  +-+---------------- 1 ----------------\n'
    1692               f'    | ValueError: 1\n'
    1693               f'    +---------------- 2 ----------------\n'
    1694               f'    | TypeError: 2\n'
    1695               f'    +------------------------------------\n')
    1696  
    1697          report = self.get_report(exc)
    1698          self.assertEqual(report, expected)
    1699  
    1700      def test_exception_group_cause(self):
    1701          def exc():
    1702              EG = ExceptionGroup
    1703              try:
    1704                  raise EG("eg1", [ValueError(1), TypeError(2)])
    1705              except Exception as e:
    1706                  raise EG("eg2", [ValueError(3), TypeError(4)]) from e
    1707  
    1708          expected = (f'  + Exception Group Traceback (most recent call last):\n'
    1709                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 3}, in exc\n'
    1710                      f'  |     raise EG("eg1", [ValueError(1), TypeError(2)])\n'
    1711                      f'  | ExceptionGroup: eg1 (2 sub-exceptions)\n'
    1712                      f'  +-+---------------- 1 ----------------\n'
    1713                      f'    | ValueError: 1\n'
    1714                      f'    +---------------- 2 ----------------\n'
    1715                      f'    | TypeError: 2\n'
    1716                      f'    +------------------------------------\n'
    1717                      f'\n'
    1718                      f'The above exception was the direct cause of the following exception:\n'
    1719                      f'\n'
    1720                      f'  + Exception Group Traceback (most recent call last):\n'
    1721                      f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
    1722                      f'  |     exception_or_callable()\n'
    1723                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
    1724                      f'  |     raise EG("eg2", [ValueError(3), TypeError(4)]) from e\n'
    1725                      f'  | ExceptionGroup: eg2 (2 sub-exceptions)\n'
    1726                      f'  +-+---------------- 1 ----------------\n'
    1727                      f'    | ValueError: 3\n'
    1728                      f'    +---------------- 2 ----------------\n'
    1729                      f'    | TypeError: 4\n'
    1730                      f'    +------------------------------------\n')
    1731  
    1732          report = self.get_report(exc)
    1733          self.assertEqual(report, expected)
    1734  
    1735      def test_exception_group_context_with_context(self):
    1736          def exc():
    1737              EG = ExceptionGroup
    1738              try:
    1739                  try:
    1740                      raise EG("eg1", [ValueError(1), TypeError(2)])
    1741                  except:
    1742                      raise EG("eg2", [ValueError(3), TypeError(4)])
    1743              except:
    1744                  raise ImportError(5)
    1745  
    1746          expected = (
    1747               f'  + Exception Group Traceback (most recent call last):\n'
    1748               f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 4}, in exc\n'
    1749               f'  |     raise EG("eg1", [ValueError(1), TypeError(2)])\n'
    1750               f'  | ExceptionGroup: eg1 (2 sub-exceptions)\n'
    1751               f'  +-+---------------- 1 ----------------\n'
    1752               f'    | ValueError: 1\n'
    1753               f'    +---------------- 2 ----------------\n'
    1754               f'    | TypeError: 2\n'
    1755               f'    +------------------------------------\n'
    1756               f'\n'
    1757               f'During handling of the above exception, another exception occurred:\n'
    1758               f'\n'
    1759               f'  + Exception Group Traceback (most recent call last):\n'
    1760               f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
    1761               f'  |     raise EG("eg2", [ValueError(3), TypeError(4)])\n'
    1762               f'  | ExceptionGroup: eg2 (2 sub-exceptions)\n'
    1763               f'  +-+---------------- 1 ----------------\n'
    1764               f'    | ValueError: 3\n'
    1765               f'    +---------------- 2 ----------------\n'
    1766               f'    | TypeError: 4\n'
    1767               f'    +------------------------------------\n'
    1768               f'\n'
    1769               f'During handling of the above exception, another exception occurred:\n'
    1770               f'\n'
    1771               f'Traceback (most recent call last):\n'
    1772               f'  File "{__file__}", line {self.callable_line}, in get_exception\n'
    1773               f'    exception_or_callable()\n'
    1774               f'  File "{__file__}", line {exc.__code__.co_firstlineno + 8}, in exc\n'
    1775               f'    raise ImportError(5)\n'
    1776               f'ImportError: 5\n')
    1777  
    1778          report = self.get_report(exc)
    1779          self.assertEqual(report, expected)
    1780  
    1781      def test_exception_group_nested(self):
    1782          def exc():
    1783              EG = ExceptionGroup
    1784              VE = ValueError
    1785              TE = TypeError
    1786              try:
    1787                  try:
    1788                      raise EG("nested", [TE(2), TE(3)])
    1789                  except Exception as e:
    1790                      exc = e
    1791                  raise EG("eg", [VE(1), exc, VE(4)])
    1792              except:
    1793                  raise EG("top", [VE(5)])
    1794  
    1795          expected = (f'  + Exception Group Traceback (most recent call last):\n'
    1796                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
    1797                      f'  |     raise EG("eg", [VE(1), exc, VE(4)])\n'
    1798                      f'  | ExceptionGroup: eg (3 sub-exceptions)\n'
    1799                      f'  +-+---------------- 1 ----------------\n'
    1800                      f'    | ValueError: 1\n'
    1801                      f'    +---------------- 2 ----------------\n'
    1802                      f'    | Exception Group Traceback (most recent call last):\n'
    1803                      f'    |   File "{__file__}", line {exc.__code__.co_firstlineno + 6}, in exc\n'
    1804                      f'    |     raise EG("nested", [TE(2), TE(3)])\n'
    1805                      f'    | ExceptionGroup: nested (2 sub-exceptions)\n'
    1806                      f'    +-+---------------- 1 ----------------\n'
    1807                      f'      | TypeError: 2\n'
    1808                      f'      +---------------- 2 ----------------\n'
    1809                      f'      | TypeError: 3\n'
    1810                      f'      +------------------------------------\n'
    1811                      f'    +---------------- 3 ----------------\n'
    1812                      f'    | ValueError: 4\n'
    1813                      f'    +------------------------------------\n'
    1814                      f'\n'
    1815                      f'During handling of the above exception, another exception occurred:\n'
    1816                      f'\n'
    1817                      f'  + Exception Group Traceback (most recent call last):\n'
    1818                      f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
    1819                      f'  |     exception_or_callable()\n'
    1820                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 11}, in exc\n'
    1821                      f'  |     raise EG("top", [VE(5)])\n'
    1822                      f'  | ExceptionGroup: top (1 sub-exception)\n'
    1823                      f'  +-+---------------- 1 ----------------\n'
    1824                      f'    | ValueError: 5\n'
    1825                      f'    +------------------------------------\n')
    1826  
    1827          report = self.get_report(exc)
    1828          self.assertEqual(report, expected)
    1829  
    1830      def test_exception_group_width_limit(self):
    1831          excs = []
    1832          for i in range(1000):
    1833              excs.append(ValueError(i))
    1834          eg = ExceptionGroup('eg', excs)
    1835  
    1836          expected = ('  | ExceptionGroup: eg (1000 sub-exceptions)\n'
    1837                      '  +-+---------------- 1 ----------------\n'
    1838                      '    | ValueError: 0\n'
    1839                      '    +---------------- 2 ----------------\n'
    1840                      '    | ValueError: 1\n'
    1841                      '    +---------------- 3 ----------------\n'
    1842                      '    | ValueError: 2\n'
    1843                      '    +---------------- 4 ----------------\n'
    1844                      '    | ValueError: 3\n'
    1845                      '    +---------------- 5 ----------------\n'
    1846                      '    | ValueError: 4\n'
    1847                      '    +---------------- 6 ----------------\n'
    1848                      '    | ValueError: 5\n'
    1849                      '    +---------------- 7 ----------------\n'
    1850                      '    | ValueError: 6\n'
    1851                      '    +---------------- 8 ----------------\n'
    1852                      '    | ValueError: 7\n'
    1853                      '    +---------------- 9 ----------------\n'
    1854                      '    | ValueError: 8\n'
    1855                      '    +---------------- 10 ----------------\n'
    1856                      '    | ValueError: 9\n'
    1857                      '    +---------------- 11 ----------------\n'
    1858                      '    | ValueError: 10\n'
    1859                      '    +---------------- 12 ----------------\n'
    1860                      '    | ValueError: 11\n'
    1861                      '    +---------------- 13 ----------------\n'
    1862                      '    | ValueError: 12\n'
    1863                      '    +---------------- 14 ----------------\n'
    1864                      '    | ValueError: 13\n'
    1865                      '    +---------------- 15 ----------------\n'
    1866                      '    | ValueError: 14\n'
    1867                      '    +---------------- ... ----------------\n'
    1868                      '    | and 985 more exceptions\n'
    1869                      '    +------------------------------------\n')
    1870  
    1871          report = self.get_report(eg)
    1872          self.assertEqual(report, expected)
    1873  
    1874      def test_exception_group_depth_limit(self):
    1875          exc = TypeError('bad type')
    1876          for i in range(1000):
    1877              exc = ExceptionGroup(
    1878                  f'eg{i}',
    1879                  [ValueError(i), exc, ValueError(-i)])
    1880  
    1881          expected = ('  | ExceptionGroup: eg999 (3 sub-exceptions)\n'
    1882                      '  +-+---------------- 1 ----------------\n'
    1883                      '    | ValueError: 999\n'
    1884                      '    +---------------- 2 ----------------\n'
    1885                      '    | ExceptionGroup: eg998 (3 sub-exceptions)\n'
    1886                      '    +-+---------------- 1 ----------------\n'
    1887                      '      | ValueError: 998\n'
    1888                      '      +---------------- 2 ----------------\n'
    1889                      '      | ExceptionGroup: eg997 (3 sub-exceptions)\n'
    1890                      '      +-+---------------- 1 ----------------\n'
    1891                      '        | ValueError: 997\n'
    1892                      '        +---------------- 2 ----------------\n'
    1893                      '        | ExceptionGroup: eg996 (3 sub-exceptions)\n'
    1894                      '        +-+---------------- 1 ----------------\n'
    1895                      '          | ValueError: 996\n'
    1896                      '          +---------------- 2 ----------------\n'
    1897                      '          | ExceptionGroup: eg995 (3 sub-exceptions)\n'
    1898                      '          +-+---------------- 1 ----------------\n'
    1899                      '            | ValueError: 995\n'
    1900                      '            +---------------- 2 ----------------\n'
    1901                      '            | ExceptionGroup: eg994 (3 sub-exceptions)\n'
    1902                      '            +-+---------------- 1 ----------------\n'
    1903                      '              | ValueError: 994\n'
    1904                      '              +---------------- 2 ----------------\n'
    1905                      '              | ExceptionGroup: eg993 (3 sub-exceptions)\n'
    1906                      '              +-+---------------- 1 ----------------\n'
    1907                      '                | ValueError: 993\n'
    1908                      '                +---------------- 2 ----------------\n'
    1909                      '                | ExceptionGroup: eg992 (3 sub-exceptions)\n'
    1910                      '                +-+---------------- 1 ----------------\n'
    1911                      '                  | ValueError: 992\n'
    1912                      '                  +---------------- 2 ----------------\n'
    1913                      '                  | ExceptionGroup: eg991 (3 sub-exceptions)\n'
    1914                      '                  +-+---------------- 1 ----------------\n'
    1915                      '                    | ValueError: 991\n'
    1916                      '                    +---------------- 2 ----------------\n'
    1917                      '                    | ExceptionGroup: eg990 (3 sub-exceptions)\n'
    1918                      '                    +-+---------------- 1 ----------------\n'
    1919                      '                      | ValueError: 990\n'
    1920                      '                      +---------------- 2 ----------------\n'
    1921                      '                      | ... (max_group_depth is 10)\n'
    1922                      '                      +---------------- 3 ----------------\n'
    1923                      '                      | ValueError: -990\n'
    1924                      '                      +------------------------------------\n'
    1925                      '                    +---------------- 3 ----------------\n'
    1926                      '                    | ValueError: -991\n'
    1927                      '                    +------------------------------------\n'
    1928                      '                  +---------------- 3 ----------------\n'
    1929                      '                  | ValueError: -992\n'
    1930                      '                  +------------------------------------\n'
    1931                      '                +---------------- 3 ----------------\n'
    1932                      '                | ValueError: -993\n'
    1933                      '                +------------------------------------\n'
    1934                      '              +---------------- 3 ----------------\n'
    1935                      '              | ValueError: -994\n'
    1936                      '              +------------------------------------\n'
    1937                      '            +---------------- 3 ----------------\n'
    1938                      '            | ValueError: -995\n'
    1939                      '            +------------------------------------\n'
    1940                      '          +---------------- 3 ----------------\n'
    1941                      '          | ValueError: -996\n'
    1942                      '          +------------------------------------\n'
    1943                      '        +---------------- 3 ----------------\n'
    1944                      '        | ValueError: -997\n'
    1945                      '        +------------------------------------\n'
    1946                      '      +---------------- 3 ----------------\n'
    1947                      '      | ValueError: -998\n'
    1948                      '      +------------------------------------\n'
    1949                      '    +---------------- 3 ----------------\n'
    1950                      '    | ValueError: -999\n'
    1951                      '    +------------------------------------\n')
    1952  
    1953          report = self.get_report(exc)
    1954          self.assertEqual(report, expected)
    1955  
    1956      def test_exception_group_with_notes(self):
    1957          def exc():
    1958              try:
    1959                  excs = []
    1960                  for msg in ['bad value', 'terrible value']:
    1961                      try:
    1962                          raise ValueError(msg)
    1963                      except ValueError as e:
    1964                          e.add_note(f'the {msg}')
    1965                          excs.append(e)
    1966                  raise ExceptionGroup("nested", excs)
    1967              except ExceptionGroup as e:
    1968                  e.add_note(('>> Multi line note\n'
    1969                              '>> Because I am such\n'
    1970                              '>> an important exception.\n'
    1971                              '>> empty lines work too\n'
    1972                              '\n'
    1973                              '(that was an empty line)'))
    1974                  raise
    1975  
    1976          expected = (f'  + Exception Group Traceback (most recent call last):\n'
    1977                      f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
    1978                      f'  |     exception_or_callable()\n'
    1979                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 9}, in exc\n'
    1980                      f'  |     raise ExceptionGroup("nested", excs)\n'
    1981                      f'  | ExceptionGroup: nested (2 sub-exceptions)\n'
    1982                      f'  | >> Multi line note\n'
    1983                      f'  | >> Because I am such\n'
    1984                      f'  | >> an important exception.\n'
    1985                      f'  | >> empty lines work too\n'
    1986                      f'  | \n'
    1987                      f'  | (that was an empty line)\n'
    1988                      f'  +-+---------------- 1 ----------------\n'
    1989                      f'    | Traceback (most recent call last):\n'
    1990                      f'    |   File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
    1991                      f'    |     raise ValueError(msg)\n'
    1992                      f'    | ValueError: bad value\n'
    1993                      f'    | the bad value\n'
    1994                      f'    +---------------- 2 ----------------\n'
    1995                      f'    | Traceback (most recent call last):\n'
    1996                      f'    |   File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
    1997                      f'    |     raise ValueError(msg)\n'
    1998                      f'    | ValueError: terrible value\n'
    1999                      f'    | the terrible value\n'
    2000                      f'    +------------------------------------\n')
    2001  
    2002          report = self.get_report(exc)
    2003          self.assertEqual(report, expected)
    2004  
    2005      def test_exception_group_with_multiple_notes(self):
    2006          def exc():
    2007              try:
    2008                  excs = []
    2009                  for msg in ['bad value', 'terrible value']:
    2010                      try:
    2011                          raise ValueError(msg)
    2012                      except ValueError as e:
    2013                          e.add_note(f'the {msg}')
    2014                          e.add_note(f'Goodbye {msg}')
    2015                          excs.append(e)
    2016                  raise ExceptionGroup("nested", excs)
    2017              except ExceptionGroup as e:
    2018                  e.add_note(('>> Multi line note\n'
    2019                              '>> Because I am such\n'
    2020                              '>> an important exception.\n'
    2021                              '>> empty lines work too\n'
    2022                              '\n'
    2023                              '(that was an empty line)'))
    2024                  e.add_note('Goodbye!')
    2025                  raise
    2026  
    2027          expected = (f'  + Exception Group Traceback (most recent call last):\n'
    2028                      f'  |   File "{__file__}", line {self.callable_line}, in get_exception\n'
    2029                      f'  |     exception_or_callable()\n'
    2030                      f'  |   File "{__file__}", line {exc.__code__.co_firstlineno + 10}, in exc\n'
    2031                      f'  |     raise ExceptionGroup("nested", excs)\n'
    2032                      f'  | ExceptionGroup: nested (2 sub-exceptions)\n'
    2033                      f'  | >> Multi line note\n'
    2034                      f'  | >> Because I am such\n'
    2035                      f'  | >> an important exception.\n'
    2036                      f'  | >> empty lines work too\n'
    2037                      f'  | \n'
    2038                      f'  | (that was an empty line)\n'
    2039                      f'  | Goodbye!\n'
    2040                      f'  +-+---------------- 1 ----------------\n'
    2041                      f'    | Traceback (most recent call last):\n'
    2042                      f'    |   File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
    2043                      f'    |     raise ValueError(msg)\n'
    2044                      f'    | ValueError: bad value\n'
    2045                      f'    | the bad value\n'
    2046                      f'    | Goodbye bad value\n'
    2047                      f'    +---------------- 2 ----------------\n'
    2048                      f'    | Traceback (most recent call last):\n'
    2049                      f'    |   File "{__file__}", line {exc.__code__.co_firstlineno + 5}, in exc\n'
    2050                      f'    |     raise ValueError(msg)\n'
    2051                      f'    | ValueError: terrible value\n'
    2052                      f'    | the terrible value\n'
    2053                      f'    | Goodbye terrible value\n'
    2054                      f'    +------------------------------------\n')
    2055  
    2056          report = self.get_report(exc)
    2057          self.assertEqual(report, expected)
    2058  
    2059      def test_KeyboardInterrupt_at_first_line_of_frame(self):
    2060          # see GH-93249
    2061          def f():
    2062              return sys._getframe()
    2063  
    2064          tb_next = None
    2065          frame = f()
    2066          lasti = 0
    2067          lineno = f.__code__.co_firstlineno
    2068          tb = types.TracebackType(tb_next, frame, lasti, lineno)
    2069  
    2070          exc = KeyboardInterrupt()
    2071          exc.__traceback__ = tb
    2072  
    2073          expected = (f'Traceback (most recent call last):\n'
    2074                      f'  File "{__file__}", line {lineno}, in f\n'
    2075                      f'    def f():\n'
    2076                      f'\n'
    2077                      f'KeyboardInterrupt\n')
    2078  
    2079          report = self.get_report(exc)
    2080          # remove trailing writespace:
    2081          report = '\n'.join([l.rstrip() for l in report.split('\n')])
    2082          self.assertEqual(report, expected)
    2083  
    2084  
    2085  class ESC[4;38;5;81mPyExcReportingTests(ESC[4;38;5;149mBaseExceptionReportingTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2086      #
    2087      # This checks reporting through the 'traceback' module, with both
    2088      # format_exception() and print_exception().
    2089      #
    2090  
    2091      def get_report(self, e):
    2092          e = self.get_exception(e)
    2093          s = ''.join(
    2094              traceback.format_exception(type(e), e, e.__traceback__))
    2095          with captured_output("stderr") as sio:
    2096              traceback.print_exception(type(e), e, e.__traceback__)
    2097          self.assertEqual(sio.getvalue(), s)
    2098          return s
    2099  
    2100  
    2101  class ESC[4;38;5;81mCExcReportingTests(ESC[4;38;5;149mBaseExceptionReportingTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2102      #
    2103      # This checks built-in reporting by the interpreter.
    2104      #
    2105  
    2106      @cpython_only
    2107      def get_report(self, e):
    2108          from _testcapi import exception_print
    2109          e = self.get_exception(e)
    2110          with captured_output("stderr") as s:
    2111              exception_print(e)
    2112          return s.getvalue()
    2113  
    2114  
    2115  class ESC[4;38;5;81mLimitTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2116  
    2117      ''' Tests for limit argument.
    2118          It's enough to test extact_tb, extract_stack and format_exception '''
    2119  
    2120      def last_raises1(self):
    2121          raise Exception('Last raised')
    2122  
    2123      def last_raises2(self):
    2124          self.last_raises1()
    2125  
    2126      def last_raises3(self):
    2127          self.last_raises2()
    2128  
    2129      def last_raises4(self):
    2130          self.last_raises3()
    2131  
    2132      def last_raises5(self):
    2133          self.last_raises4()
    2134  
    2135      def last_returns_frame1(self):
    2136          return sys._getframe()
    2137  
    2138      def last_returns_frame2(self):
    2139          return self.last_returns_frame1()
    2140  
    2141      def last_returns_frame3(self):
    2142          return self.last_returns_frame2()
    2143  
    2144      def last_returns_frame4(self):
    2145          return self.last_returns_frame3()
    2146  
    2147      def last_returns_frame5(self):
    2148          return self.last_returns_frame4()
    2149  
    2150      def test_extract_stack(self):
    2151          frame = self.last_returns_frame5()
    2152          def extract(**kwargs):
    2153              return traceback.extract_stack(frame, **kwargs)
    2154          def assertEqualExcept(actual, expected, ignore):
    2155              self.assertEqual(actual[:ignore], expected[:ignore])
    2156              self.assertEqual(actual[ignore+1:], expected[ignore+1:])
    2157              self.assertEqual(len(actual), len(expected))
    2158  
    2159          with support.swap_attr(sys, 'tracebacklimit', 1000):
    2160              nolim = extract()
    2161              self.assertGreater(len(nolim), 5)
    2162              self.assertEqual(extract(limit=2), nolim[-2:])
    2163              assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
    2164              self.assertEqual(extract(limit=-2), nolim[:2])
    2165              assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
    2166              self.assertEqual(extract(limit=0), [])
    2167              del sys.tracebacklimit
    2168              assertEqualExcept(extract(), nolim, -5-1)
    2169              sys.tracebacklimit = 2
    2170              self.assertEqual(extract(), nolim[-2:])
    2171              self.assertEqual(extract(limit=3), nolim[-3:])
    2172              self.assertEqual(extract(limit=-3), nolim[:3])
    2173              sys.tracebacklimit = 0
    2174              self.assertEqual(extract(), [])
    2175              sys.tracebacklimit = -1
    2176              self.assertEqual(extract(), [])
    2177  
    2178      def test_extract_tb(self):
    2179          try:
    2180              self.last_raises5()
    2181          except Exception:
    2182              exc_type, exc_value, tb = sys.exc_info()
    2183          def extract(**kwargs):
    2184              return traceback.extract_tb(tb, **kwargs)
    2185  
    2186          with support.swap_attr(sys, 'tracebacklimit', 1000):
    2187              nolim = extract()
    2188              self.assertEqual(len(nolim), 5+1)
    2189              self.assertEqual(extract(limit=2), nolim[:2])
    2190              self.assertEqual(extract(limit=10), nolim)
    2191              self.assertEqual(extract(limit=-2), nolim[-2:])
    2192              self.assertEqual(extract(limit=-10), nolim)
    2193              self.assertEqual(extract(limit=0), [])
    2194              del sys.tracebacklimit
    2195              self.assertEqual(extract(), nolim)
    2196              sys.tracebacklimit = 2
    2197              self.assertEqual(extract(), nolim[:2])
    2198              self.assertEqual(extract(limit=3), nolim[:3])
    2199              self.assertEqual(extract(limit=-3), nolim[-3:])
    2200              sys.tracebacklimit = 0
    2201              self.assertEqual(extract(), [])
    2202              sys.tracebacklimit = -1
    2203              self.assertEqual(extract(), [])
    2204  
    2205      def test_format_exception(self):
    2206          try:
    2207              self.last_raises5()
    2208          except Exception:
    2209              exc_type, exc_value, tb = sys.exc_info()
    2210          # [1:-1] to exclude "Traceback (...)" header and
    2211          # exception type and value
    2212          def extract(**kwargs):
    2213              return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
    2214  
    2215          with support.swap_attr(sys, 'tracebacklimit', 1000):
    2216              nolim = extract()
    2217              self.assertEqual(len(nolim), 5+1)
    2218              self.assertEqual(extract(limit=2), nolim[:2])
    2219              self.assertEqual(extract(limit=10), nolim)
    2220              self.assertEqual(extract(limit=-2), nolim[-2:])
    2221              self.assertEqual(extract(limit=-10), nolim)
    2222              self.assertEqual(extract(limit=0), [])
    2223              del sys.tracebacklimit
    2224              self.assertEqual(extract(), nolim)
    2225              sys.tracebacklimit = 2
    2226              self.assertEqual(extract(), nolim[:2])
    2227              self.assertEqual(extract(limit=3), nolim[:3])
    2228              self.assertEqual(extract(limit=-3), nolim[-3:])
    2229              sys.tracebacklimit = 0
    2230              self.assertEqual(extract(), [])
    2231              sys.tracebacklimit = -1
    2232              self.assertEqual(extract(), [])
    2233  
    2234  
    2235  class ESC[4;38;5;81mMiscTracebackCases(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2236      #
    2237      # Check non-printing functions in traceback module
    2238      #
    2239  
    2240      def test_clear(self):
    2241          def outer():
    2242              middle()
    2243          def middle():
    2244              inner()
    2245          def inner():
    2246              i = 1
    2247              1/0
    2248  
    2249          try:
    2250              outer()
    2251          except:
    2252              type_, value, tb = sys.exc_info()
    2253  
    2254          # Initial assertion: there's one local in the inner frame.
    2255          inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
    2256          self.assertEqual(len(inner_frame.f_locals), 1)
    2257  
    2258          # Clear traceback frames
    2259          traceback.clear_frames(tb)
    2260  
    2261          # Local variable dict should now be empty.
    2262          self.assertEqual(len(inner_frame.f_locals), 0)
    2263  
    2264      def test_extract_stack(self):
    2265          def extract():
    2266              return traceback.extract_stack()
    2267          result = extract()
    2268          lineno = extract.__code__.co_firstlineno
    2269          self.assertEqual(result[-2:], [
    2270              (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
    2271              (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
    2272              ])
    2273          self.assertEqual(len(result[0]), 4)
    2274  
    2275  
    2276  class ESC[4;38;5;81mTestFrame(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2277  
    2278      def test_basics(self):
    2279          linecache.clearcache()
    2280          linecache.lazycache("f", globals())
    2281          f = traceback.FrameSummary("f", 1, "dummy")
    2282          self.assertEqual(f,
    2283              ("f", 1, "dummy", '"""Test cases for traceback module"""'))
    2284          self.assertEqual(tuple(f),
    2285              ("f", 1, "dummy", '"""Test cases for traceback module"""'))
    2286          self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
    2287          self.assertEqual(f, tuple(f))
    2288          # Since tuple.__eq__ doesn't support FrameSummary, the equality
    2289          # operator fallbacks to FrameSummary.__eq__.
    2290          self.assertEqual(tuple(f), f)
    2291          self.assertIsNone(f.locals)
    2292          self.assertNotEqual(f, object())
    2293          self.assertEqual(f, ALWAYS_EQ)
    2294  
    2295      def test_lazy_lines(self):
    2296          linecache.clearcache()
    2297          f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
    2298          self.assertEqual(None, f._line)
    2299          linecache.lazycache("f", globals())
    2300          self.assertEqual(
    2301              '"""Test cases for traceback module"""',
    2302              f.line)
    2303  
    2304      def test_no_line(self):
    2305          f = traceback.FrameSummary("f", None, "dummy")
    2306          self.assertEqual(f.line, None)
    2307  
    2308      def test_explicit_line(self):
    2309          f = traceback.FrameSummary("f", 1, "dummy", line="line")
    2310          self.assertEqual("line", f.line)
    2311  
    2312      def test_len(self):
    2313          f = traceback.FrameSummary("f", 1, "dummy", line="line")
    2314          self.assertEqual(len(f), 4)
    2315  
    2316  
    2317  class ESC[4;38;5;81mTestStack(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2318  
    2319      def test_walk_stack(self):
    2320          def deeper():
    2321              return list(traceback.walk_stack(None))
    2322          s1 = list(traceback.walk_stack(None))
    2323          s2 = deeper()
    2324          self.assertEqual(len(s2) - len(s1), 1)
    2325          self.assertEqual(s2[1:], s1)
    2326  
    2327      def test_walk_tb(self):
    2328          try:
    2329              1/0
    2330          except Exception:
    2331              _, _, tb = sys.exc_info()
    2332          s = list(traceback.walk_tb(tb))
    2333          self.assertEqual(len(s), 1)
    2334  
    2335      def test_extract_stack(self):
    2336          s = traceback.StackSummary.extract(traceback.walk_stack(None))
    2337          self.assertIsInstance(s, traceback.StackSummary)
    2338  
    2339      def test_extract_stack_limit(self):
    2340          s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
    2341          self.assertEqual(len(s), 5)
    2342  
    2343      def test_extract_stack_lookup_lines(self):
    2344          linecache.clearcache()
    2345          linecache.updatecache('/foo.py', globals())
    2346          c = test_code('/foo.py', 'method')
    2347          f = test_frame(c, None, None)
    2348          s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
    2349          linecache.clearcache()
    2350          self.assertEqual(s[0].line, "import sys")
    2351  
    2352      def test_extract_stackup_deferred_lookup_lines(self):
    2353          linecache.clearcache()
    2354          c = test_code('/foo.py', 'method')
    2355          f = test_frame(c, None, None)
    2356          s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
    2357          self.assertEqual({}, linecache.cache)
    2358          linecache.updatecache('/foo.py', globals())
    2359          self.assertEqual(s[0].line, "import sys")
    2360  
    2361      def test_from_list(self):
    2362          s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
    2363          self.assertEqual(
    2364              ['  File "foo.py", line 1, in fred\n    line\n'],
    2365              s.format())
    2366  
    2367      def test_from_list_edited_stack(self):
    2368          s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
    2369          s[0] = ('foo.py', 2, 'fred', 'line')
    2370          s2 = traceback.StackSummary.from_list(s)
    2371          self.assertEqual(
    2372              ['  File "foo.py", line 2, in fred\n    line\n'],
    2373              s2.format())
    2374  
    2375      def test_format_smoke(self):
    2376          # For detailed tests see the format_list tests, which consume the same
    2377          # code.
    2378          s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
    2379          self.assertEqual(
    2380              ['  File "foo.py", line 1, in fred\n    line\n'],
    2381              s.format())
    2382  
    2383      def test_locals(self):
    2384          linecache.updatecache('/foo.py', globals())
    2385          c = test_code('/foo.py', 'method')
    2386          f = test_frame(c, globals(), {'something': 1})
    2387          s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
    2388          self.assertEqual(s[0].locals, {'something': '1'})
    2389  
    2390      def test_no_locals(self):
    2391          linecache.updatecache('/foo.py', globals())
    2392          c = test_code('/foo.py', 'method')
    2393          f = test_frame(c, globals(), {'something': 1})
    2394          s = traceback.StackSummary.extract(iter([(f, 6)]))
    2395          self.assertEqual(s[0].locals, None)
    2396  
    2397      def test_format_locals(self):
    2398          def some_inner(k, v):
    2399              a = 1
    2400              b = 2
    2401              return traceback.StackSummary.extract(
    2402                  traceback.walk_stack(None), capture_locals=True, limit=1)
    2403          s = some_inner(3, 4)
    2404          self.assertEqual(
    2405              ['  File "%s", line %d, in some_inner\n'
    2406               '    return traceback.StackSummary.extract(\n'
    2407               '    a = 1\n'
    2408               '    b = 2\n'
    2409               '    k = 3\n'
    2410               '    v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
    2411              ], s.format())
    2412  
    2413      def test_custom_format_frame(self):
    2414          class ESC[4;38;5;81mCustomStackSummary(ESC[4;38;5;149mtracebackESC[4;38;5;149m.ESC[4;38;5;149mStackSummary):
    2415              def format_frame_summary(self, frame_summary):
    2416                  return f'{frame_summary.filename}:{frame_summary.lineno}'
    2417  
    2418          def some_inner():
    2419              return CustomStackSummary.extract(
    2420                  traceback.walk_stack(None), limit=1)
    2421  
    2422          s = some_inner()
    2423          self.assertEqual(
    2424              s.format(),
    2425              [f'{__file__}:{some_inner.__code__.co_firstlineno + 1}'])
    2426  
    2427      def test_dropping_frames(self):
    2428          def f():
    2429              1/0
    2430  
    2431          def g():
    2432              try:
    2433                  f()
    2434              except:
    2435                  return sys.exc_info()
    2436  
    2437          exc_info = g()
    2438  
    2439          class ESC[4;38;5;81mSkip_G(ESC[4;38;5;149mtracebackESC[4;38;5;149m.ESC[4;38;5;149mStackSummary):
    2440              def format_frame_summary(self, frame_summary):
    2441                  if frame_summary.name == 'g':
    2442                      return None
    2443                  return super().format_frame_summary(frame_summary)
    2444  
    2445          stack = Skip_G.extract(
    2446              traceback.walk_tb(exc_info[2])).format()
    2447  
    2448          self.assertEqual(len(stack), 1)
    2449          lno = f.__code__.co_firstlineno + 1
    2450          self.assertEqual(
    2451              stack[0],
    2452              f'  File "{__file__}", line {lno}, in f\n    1/0\n'
    2453          )
    2454  
    2455  
    2456  class ESC[4;38;5;81mTestTracebackException(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2457  
    2458      def test_smoke(self):
    2459          try:
    2460              1/0
    2461          except Exception:
    2462              exc_info = sys.exc_info()
    2463              exc = traceback.TracebackException(*exc_info)
    2464              expected_stack = traceback.StackSummary.extract(
    2465                  traceback.walk_tb(exc_info[2]))
    2466          self.assertEqual(None, exc.__cause__)
    2467          self.assertEqual(None, exc.__context__)
    2468          self.assertEqual(False, exc.__suppress_context__)
    2469          self.assertEqual(expected_stack, exc.stack)
    2470          self.assertEqual(exc_info[0], exc.exc_type)
    2471          self.assertEqual(str(exc_info[1]), str(exc))
    2472  
    2473      def test_from_exception(self):
    2474          # Check all the parameters are accepted.
    2475          def foo():
    2476              1/0
    2477          try:
    2478              foo()
    2479          except Exception as e:
    2480              exc_info = sys.exc_info()
    2481              self.expected_stack = traceback.StackSummary.extract(
    2482                  traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
    2483                  capture_locals=True)
    2484              self.exc = traceback.TracebackException.from_exception(
    2485                  e, limit=1, lookup_lines=False, capture_locals=True)
    2486          expected_stack = self.expected_stack
    2487          exc = self.exc
    2488          self.assertEqual(None, exc.__cause__)
    2489          self.assertEqual(None, exc.__context__)
    2490          self.assertEqual(False, exc.__suppress_context__)
    2491          self.assertEqual(expected_stack, exc.stack)
    2492          self.assertEqual(exc_info[0], exc.exc_type)
    2493          self.assertEqual(str(exc_info[1]), str(exc))
    2494  
    2495      def test_cause(self):
    2496          try:
    2497              try:
    2498                  1/0
    2499              finally:
    2500                  exc_info_context = sys.exc_info()
    2501                  exc_context = traceback.TracebackException(*exc_info_context)
    2502                  cause = Exception("cause")
    2503                  raise Exception("uh oh") from cause
    2504          except Exception:
    2505              exc_info = sys.exc_info()
    2506              exc = traceback.TracebackException(*exc_info)
    2507              expected_stack = traceback.StackSummary.extract(
    2508                  traceback.walk_tb(exc_info[2]))
    2509          exc_cause = traceback.TracebackException(Exception, cause, None)
    2510          self.assertEqual(exc_cause, exc.__cause__)
    2511          self.assertEqual(exc_context, exc.__context__)
    2512          self.assertEqual(True, exc.__suppress_context__)
    2513          self.assertEqual(expected_stack, exc.stack)
    2514          self.assertEqual(exc_info[0], exc.exc_type)
    2515          self.assertEqual(str(exc_info[1]), str(exc))
    2516  
    2517      def test_context(self):
    2518          try:
    2519              try:
    2520                  1/0
    2521              finally:
    2522                  exc_info_context = sys.exc_info()
    2523                  exc_context = traceback.TracebackException(*exc_info_context)
    2524                  raise Exception("uh oh")
    2525          except Exception:
    2526              exc_info = sys.exc_info()
    2527              exc = traceback.TracebackException(*exc_info)
    2528              expected_stack = traceback.StackSummary.extract(
    2529                  traceback.walk_tb(exc_info[2]))
    2530          self.assertEqual(None, exc.__cause__)
    2531          self.assertEqual(exc_context, exc.__context__)
    2532          self.assertEqual(False, exc.__suppress_context__)
    2533          self.assertEqual(expected_stack, exc.stack)
    2534          self.assertEqual(exc_info[0], exc.exc_type)
    2535          self.assertEqual(str(exc_info[1]), str(exc))
    2536  
    2537      def test_long_context_chain(self):
    2538          def f():
    2539              try:
    2540                  1/0
    2541              except:
    2542                  f()
    2543  
    2544          try:
    2545              f()
    2546          except RecursionError:
    2547              exc_info = sys.exc_info()
    2548          else:
    2549              self.fail("Exception not raised")
    2550  
    2551          te = traceback.TracebackException(*exc_info)
    2552          res = list(te.format())
    2553  
    2554          # many ZeroDiv errors followed by the RecursionError
    2555          self.assertGreater(len(res), sys.getrecursionlimit())
    2556          self.assertGreater(
    2557              len([l for l in res if 'ZeroDivisionError:' in l]),
    2558              sys.getrecursionlimit() * 0.5)
    2559          self.assertIn(
    2560              "RecursionError: maximum recursion depth exceeded", res[-1])
    2561  
    2562      def test_compact_with_cause(self):
    2563          try:
    2564              try:
    2565                  1/0
    2566              finally:
    2567                  cause = Exception("cause")
    2568                  raise Exception("uh oh") from cause
    2569          except Exception:
    2570              exc_info = sys.exc_info()
    2571              exc = traceback.TracebackException(*exc_info, compact=True)
    2572              expected_stack = traceback.StackSummary.extract(
    2573                  traceback.walk_tb(exc_info[2]))
    2574          exc_cause = traceback.TracebackException(Exception, cause, None)
    2575          self.assertEqual(exc_cause, exc.__cause__)
    2576          self.assertEqual(None, exc.__context__)
    2577          self.assertEqual(True, exc.__suppress_context__)
    2578          self.assertEqual(expected_stack, exc.stack)
    2579          self.assertEqual(exc_info[0], exc.exc_type)
    2580          self.assertEqual(str(exc_info[1]), str(exc))
    2581  
    2582      def test_compact_no_cause(self):
    2583          try:
    2584              try:
    2585                  1/0
    2586              finally:
    2587                  exc_info_context = sys.exc_info()
    2588                  exc_context = traceback.TracebackException(*exc_info_context)
    2589                  raise Exception("uh oh")
    2590          except Exception:
    2591              exc_info = sys.exc_info()
    2592              exc = traceback.TracebackException(*exc_info, compact=True)
    2593              expected_stack = traceback.StackSummary.extract(
    2594                  traceback.walk_tb(exc_info[2]))
    2595          self.assertEqual(None, exc.__cause__)
    2596          self.assertEqual(exc_context, exc.__context__)
    2597          self.assertEqual(False, exc.__suppress_context__)
    2598          self.assertEqual(expected_stack, exc.stack)
    2599          self.assertEqual(exc_info[0], exc.exc_type)
    2600          self.assertEqual(str(exc_info[1]), str(exc))
    2601  
    2602      def test_no_refs_to_exception_and_traceback_objects(self):
    2603          try:
    2604              1/0
    2605          except Exception:
    2606              exc_info = sys.exc_info()
    2607  
    2608          refcnt1 = sys.getrefcount(exc_info[1])
    2609          refcnt2 = sys.getrefcount(exc_info[2])
    2610          exc = traceback.TracebackException(*exc_info)
    2611          self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
    2612          self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
    2613  
    2614      def test_comparison_basic(self):
    2615          try:
    2616              1/0
    2617          except Exception:
    2618              exc_info = sys.exc_info()
    2619              exc = traceback.TracebackException(*exc_info)
    2620              exc2 = traceback.TracebackException(*exc_info)
    2621          self.assertIsNot(exc, exc2)
    2622          self.assertEqual(exc, exc2)
    2623          self.assertNotEqual(exc, object())
    2624          self.assertEqual(exc, ALWAYS_EQ)
    2625  
    2626      def test_comparison_params_variations(self):
    2627          def raise_exc():
    2628              try:
    2629                  raise ValueError('bad value')
    2630              except:
    2631                  raise
    2632  
    2633          def raise_with_locals():
    2634              x, y = 1, 2
    2635              raise_exc()
    2636  
    2637          try:
    2638              raise_with_locals()
    2639          except Exception:
    2640              exc_info = sys.exc_info()
    2641  
    2642          exc = traceback.TracebackException(*exc_info)
    2643          exc1 = traceback.TracebackException(*exc_info, limit=10)
    2644          exc2 = traceback.TracebackException(*exc_info, limit=2)
    2645  
    2646          self.assertEqual(exc, exc1)      # limit=10 gets all frames
    2647          self.assertNotEqual(exc, exc2)   # limit=2 truncates the output
    2648  
    2649          # locals change the output
    2650          exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
    2651          self.assertNotEqual(exc, exc3)
    2652  
    2653          # there are no locals in the innermost frame
    2654          exc4 = traceback.TracebackException(*exc_info, limit=-1)
    2655          exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
    2656          self.assertEqual(exc4, exc5)
    2657  
    2658          # there are locals in the next-to-innermost frame
    2659          exc6 = traceback.TracebackException(*exc_info, limit=-2)
    2660          exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
    2661          self.assertNotEqual(exc6, exc7)
    2662  
    2663      def test_comparison_equivalent_exceptions_are_equal(self):
    2664          excs = []
    2665          for _ in range(2):
    2666              try:
    2667                  1/0
    2668              except:
    2669                  excs.append(traceback.TracebackException(*sys.exc_info()))
    2670          self.assertEqual(excs[0], excs[1])
    2671          self.assertEqual(list(excs[0].format()), list(excs[1].format()))
    2672  
    2673      def test_unhashable(self):
    2674          class ESC[4;38;5;81mUnhashableException(ESC[4;38;5;149mException):
    2675              def __eq__(self, other):
    2676                  return True
    2677  
    2678          ex1 = UnhashableException('ex1')
    2679          ex2 = UnhashableException('ex2')
    2680          try:
    2681              raise ex2 from ex1
    2682          except UnhashableException:
    2683              try:
    2684                  raise ex1
    2685              except UnhashableException:
    2686                  exc_info = sys.exc_info()
    2687          exc = traceback.TracebackException(*exc_info)
    2688          formatted = list(exc.format())
    2689          self.assertIn('UnhashableException: ex2\n', formatted[2])
    2690          self.assertIn('UnhashableException: ex1\n', formatted[6])
    2691  
    2692      def test_limit(self):
    2693          def recurse(n):
    2694              if n:
    2695                  recurse(n-1)
    2696              else:
    2697                  1/0
    2698          try:
    2699              recurse(10)
    2700          except Exception:
    2701              exc_info = sys.exc_info()
    2702              exc = traceback.TracebackException(*exc_info, limit=5)
    2703              expected_stack = traceback.StackSummary.extract(
    2704                  traceback.walk_tb(exc_info[2]), limit=5)
    2705          self.assertEqual(expected_stack, exc.stack)
    2706  
    2707      def test_lookup_lines(self):
    2708          linecache.clearcache()
    2709          e = Exception("uh oh")
    2710          c = test_code('/foo.py', 'method')
    2711          f = test_frame(c, None, None)
    2712          tb = test_tb(f, 6, None, 0)
    2713          exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
    2714          self.assertEqual(linecache.cache, {})
    2715          linecache.updatecache('/foo.py', globals())
    2716          self.assertEqual(exc.stack[0].line, "import sys")
    2717  
    2718      def test_locals(self):
    2719          linecache.updatecache('/foo.py', globals())
    2720          e = Exception("uh oh")
    2721          c = test_code('/foo.py', 'method')
    2722          f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
    2723          tb = test_tb(f, 6, None, 0)
    2724          exc = traceback.TracebackException(
    2725              Exception, e, tb, capture_locals=True)
    2726          self.assertEqual(
    2727              exc.stack[0].locals, {'something': '1', 'other': "'string'"})
    2728  
    2729      def test_no_locals(self):
    2730          linecache.updatecache('/foo.py', globals())
    2731          e = Exception("uh oh")
    2732          c = test_code('/foo.py', 'method')
    2733          f = test_frame(c, globals(), {'something': 1})
    2734          tb = test_tb(f, 6, None, 0)
    2735          exc = traceback.TracebackException(Exception, e, tb)
    2736          self.assertEqual(exc.stack[0].locals, None)
    2737  
    2738      def test_traceback_header(self):
    2739          # do not print a traceback header if exc_traceback is None
    2740          # see issue #24695
    2741          exc = traceback.TracebackException(Exception, Exception("haven"), None)
    2742          self.assertEqual(list(exc.format()), ["Exception: haven\n"])
    2743  
    2744      @requires_debug_ranges()
    2745      def test_print(self):
    2746          def f():
    2747              x = 12
    2748              try:
    2749                  x/0
    2750              except Exception:
    2751                  return sys.exc_info()
    2752          exc = traceback.TracebackException(*f(), capture_locals=True)
    2753          output = StringIO()
    2754          exc.print(file=output)
    2755          self.assertEqual(
    2756              output.getvalue().split('\n')[-5:],
    2757              ['    x/0',
    2758               '    ~^~',
    2759               '    x = 12',
    2760               'ZeroDivisionError: division by zero',
    2761               ''])
    2762  
    2763  
    2764  class ESC[4;38;5;81mTestTracebackException_ExceptionGroups(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2765      def setUp(self):
    2766          super().setUp()
    2767          self.eg_info = self._get_exception_group()
    2768  
    2769      def _get_exception_group(self):
    2770          def f():
    2771              1/0
    2772  
    2773          def g(v):
    2774              raise ValueError(v)
    2775  
    2776          self.lno_f = f.__code__.co_firstlineno
    2777          self.lno_g = g.__code__.co_firstlineno
    2778  
    2779          try:
    2780              try:
    2781                  try:
    2782                      f()
    2783                  except Exception as e:
    2784                      exc1 = e
    2785                  try:
    2786                      g(42)
    2787                  except Exception as e:
    2788                      exc2 = e
    2789                  raise ExceptionGroup("eg1", [exc1, exc2])
    2790              except ExceptionGroup as e:
    2791                  exc3 = e
    2792              try:
    2793                  g(24)
    2794              except Exception as e:
    2795                  exc4 = e
    2796              raise ExceptionGroup("eg2", [exc3, exc4])
    2797          except ExceptionGroup:
    2798              return sys.exc_info()
    2799          self.fail('Exception Not Raised')
    2800  
    2801      def test_exception_group_construction(self):
    2802          eg_info = self.eg_info
    2803          teg1 = traceback.TracebackException(*eg_info)
    2804          teg2 = traceback.TracebackException.from_exception(eg_info[1])
    2805          self.assertIsNot(teg1, teg2)
    2806          self.assertEqual(teg1, teg2)
    2807  
    2808      def test_exception_group_format_exception_only(self):
    2809          teg = traceback.TracebackException(*self.eg_info)
    2810          formatted = ''.join(teg.format_exception_only()).split('\n')
    2811          expected = "ExceptionGroup: eg2 (2 sub-exceptions)\n".split('\n')
    2812  
    2813          self.assertEqual(formatted, expected)
    2814  
    2815      def test_exception_group_format(self):
    2816          teg = traceback.TracebackException(*self.eg_info)
    2817  
    2818          formatted = ''.join(teg.format()).split('\n')
    2819          lno_f = self.lno_f
    2820          lno_g = self.lno_g
    2821  
    2822          expected = [
    2823                      f'  + Exception Group Traceback (most recent call last):',
    2824                      f'  |   File "{__file__}", line {lno_g+23}, in _get_exception_group',
    2825                      f'  |     raise ExceptionGroup("eg2", [exc3, exc4])',
    2826                      f'  | ExceptionGroup: eg2 (2 sub-exceptions)',
    2827                      f'  +-+---------------- 1 ----------------',
    2828                      f'    | Exception Group Traceback (most recent call last):',
    2829                      f'    |   File "{__file__}", line {lno_g+16}, in _get_exception_group',
    2830                      f'    |     raise ExceptionGroup("eg1", [exc1, exc2])',
    2831                      f'    | ExceptionGroup: eg1 (2 sub-exceptions)',
    2832                      f'    +-+---------------- 1 ----------------',
    2833                      f'      | Traceback (most recent call last):',
    2834                      f'      |   File "{__file__}", line {lno_g+9}, in _get_exception_group',
    2835                      f'      |     f()',
    2836                      f'      |   File "{__file__}", line {lno_f+1}, in f',
    2837                      f'      |     1/0',
    2838                      f'      |     ~^~',
    2839                      f'      | ZeroDivisionError: division by zero',
    2840                      f'      +---------------- 2 ----------------',
    2841                      f'      | Traceback (most recent call last):',
    2842                      f'      |   File "{__file__}", line {lno_g+13}, in _get_exception_group',
    2843                      f'      |     g(42)',
    2844                      f'      |   File "{__file__}", line {lno_g+1}, in g',
    2845                      f'      |     raise ValueError(v)',
    2846                      f'      | ValueError: 42',
    2847                      f'      +------------------------------------',
    2848                      f'    +---------------- 2 ----------------',
    2849                      f'    | Traceback (most recent call last):',
    2850                      f'    |   File "{__file__}", line {lno_g+20}, in _get_exception_group',
    2851                      f'    |     g(24)',
    2852                      f'    |   File "{__file__}", line {lno_g+1}, in g',
    2853                      f'    |     raise ValueError(v)',
    2854                      f'    | ValueError: 24',
    2855                      f'    +------------------------------------',
    2856                      f'']
    2857  
    2858          self.assertEqual(formatted, expected)
    2859  
    2860      def test_max_group_width(self):
    2861          excs1 = []
    2862          excs2 = []
    2863          for i in range(3):
    2864              excs1.append(ValueError(i))
    2865          for i in range(10):
    2866              excs2.append(TypeError(i))
    2867  
    2868          EG = ExceptionGroup
    2869          eg = EG('eg', [EG('eg1', excs1), EG('eg2', excs2)])
    2870  
    2871          teg = traceback.TracebackException.from_exception(eg, max_group_width=2)
    2872          formatted = ''.join(teg.format()).split('\n')
    2873  
    2874          expected = [
    2875                      f'  | ExceptionGroup: eg (2 sub-exceptions)',
    2876                      f'  +-+---------------- 1 ----------------',
    2877                      f'    | ExceptionGroup: eg1 (3 sub-exceptions)',
    2878                      f'    +-+---------------- 1 ----------------',
    2879                      f'      | ValueError: 0',
    2880                      f'      +---------------- 2 ----------------',
    2881                      f'      | ValueError: 1',
    2882                      f'      +---------------- ... ----------------',
    2883                      f'      | and 1 more exception',
    2884                      f'      +------------------------------------',
    2885                      f'    +---------------- 2 ----------------',
    2886                      f'    | ExceptionGroup: eg2 (10 sub-exceptions)',
    2887                      f'    +-+---------------- 1 ----------------',
    2888                      f'      | TypeError: 0',
    2889                      f'      +---------------- 2 ----------------',
    2890                      f'      | TypeError: 1',
    2891                      f'      +---------------- ... ----------------',
    2892                      f'      | and 8 more exceptions',
    2893                      f'      +------------------------------------',
    2894                      f'']
    2895  
    2896          self.assertEqual(formatted, expected)
    2897  
    2898      def test_max_group_depth(self):
    2899          exc = TypeError('bad type')
    2900          for i in range(3):
    2901              exc = ExceptionGroup('exc', [ValueError(-i), exc, ValueError(i)])
    2902  
    2903          teg = traceback.TracebackException.from_exception(exc, max_group_depth=2)
    2904          formatted = ''.join(teg.format()).split('\n')
    2905  
    2906          expected = [
    2907                      f'  | ExceptionGroup: exc (3 sub-exceptions)',
    2908                      f'  +-+---------------- 1 ----------------',
    2909                      f'    | ValueError: -2',
    2910                      f'    +---------------- 2 ----------------',
    2911                      f'    | ExceptionGroup: exc (3 sub-exceptions)',
    2912                      f'    +-+---------------- 1 ----------------',
    2913                      f'      | ValueError: -1',
    2914                      f'      +---------------- 2 ----------------',
    2915                      f'      | ... (max_group_depth is 2)',
    2916                      f'      +---------------- 3 ----------------',
    2917                      f'      | ValueError: 1',
    2918                      f'      +------------------------------------',
    2919                      f'    +---------------- 3 ----------------',
    2920                      f'    | ValueError: 2',
    2921                      f'    +------------------------------------',
    2922                      f'']
    2923  
    2924          self.assertEqual(formatted, expected)
    2925  
    2926      def test_comparison(self):
    2927          try:
    2928              raise self.eg_info[1]
    2929          except ExceptionGroup:
    2930              exc_info = sys.exc_info()
    2931          for _ in range(5):
    2932              try:
    2933                  raise exc_info[1]
    2934              except:
    2935                  exc_info = sys.exc_info()
    2936          exc = traceback.TracebackException(*exc_info)
    2937          exc2 = traceback.TracebackException(*exc_info)
    2938          exc3 = traceback.TracebackException(*exc_info, limit=300)
    2939          ne = traceback.TracebackException(*exc_info, limit=3)
    2940          self.assertIsNot(exc, exc2)
    2941          self.assertEqual(exc, exc2)
    2942          self.assertEqual(exc, exc3)
    2943          self.assertNotEqual(exc, ne)
    2944          self.assertNotEqual(exc, object())
    2945          self.assertEqual(exc, ALWAYS_EQ)
    2946  
    2947  
    2948  class ESC[4;38;5;81mMiscTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2949  
    2950      def test_all(self):
    2951          expected = set()
    2952          denylist = {'print_list'}
    2953          for name in dir(traceback):
    2954              if name.startswith('_') or name in denylist:
    2955                  continue
    2956              module_object = getattr(traceback, name)
    2957              if getattr(module_object, '__module__', None) == 'traceback':
    2958                  expected.add(name)
    2959          self.assertCountEqual(traceback.__all__, expected)
    2960  
    2961  
    2962  if __name__ == "__main__":
    2963      unittest.main()