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