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