• 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 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        # last line can have additional text appended
348        self.assertIn(expected[-1], actual[-1])
349
350        # Check the recursion count is roughly as expected
351        rec_limit = sys.getrecursionlimit()
352        self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit))
353
354        # Check a known (limited) number of recursive invocations
355        def g(count=10):
356            if count:
357                return g(count-1)
358            raise ValueError
359
360        with captured_output("stderr") as stderr_g:
361            try:
362                g()
363            except ValueError as exc:
364                render_exc()
365            else:
366                self.fail("no value error was raised")
367
368        lineno_g = g.__code__.co_firstlineno
369        result_g = (
370            f'  File "{__file__}", line {lineno_g+2}, in g\n'
371            '    return g(count-1)\n'
372            f'  File "{__file__}", line {lineno_g+2}, in g\n'
373            '    return g(count-1)\n'
374            f'  File "{__file__}", line {lineno_g+2}, in g\n'
375            '    return g(count-1)\n'
376            '  [Previous line repeated 7 more times]\n'
377            f'  File "{__file__}", line {lineno_g+3}, in g\n'
378            '    raise ValueError\n'
379            'ValueError\n'
380        )
381        tb_line = (
382            'Traceback (most recent call last):\n'
383            f'  File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n'
384            '    g()\n'
385        )
386        expected = (tb_line + result_g).splitlines()
387        actual = stderr_g.getvalue().splitlines()
388        self.assertEqual(actual, expected)
389
390        # Check 2 different repetitive sections
391        def h(count=10):
392            if count:
393                return h(count-1)
394            g()
395
396        with captured_output("stderr") as stderr_h:
397            try:
398                h()
399            except ValueError as exc:
400                render_exc()
401            else:
402                self.fail("no value error was raised")
403
404        lineno_h = h.__code__.co_firstlineno
405        result_h = (
406            'Traceback (most recent call last):\n'
407            f'  File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n'
408            '    h()\n'
409            f'  File "{__file__}", line {lineno_h+2}, in h\n'
410            '    return h(count-1)\n'
411            f'  File "{__file__}", line {lineno_h+2}, in h\n'
412            '    return h(count-1)\n'
413            f'  File "{__file__}", line {lineno_h+2}, in h\n'
414            '    return h(count-1)\n'
415            '  [Previous line repeated 7 more times]\n'
416            f'  File "{__file__}", line {lineno_h+3}, in h\n'
417            '    g()\n'
418        )
419        expected = (result_h + result_g).splitlines()
420        actual = stderr_h.getvalue().splitlines()
421        self.assertEqual(actual, expected)
422
423        # Check the boundary conditions. First, test just below the cutoff.
424        with captured_output("stderr") as stderr_g:
425            try:
426                g(traceback._RECURSIVE_CUTOFF)
427            except ValueError as exc:
428                render_exc()
429            else:
430                self.fail("no error raised")
431        result_g = (
432            f'  File "{__file__}", line {lineno_g+2}, in g\n'
433            '    return g(count-1)\n'
434            f'  File "{__file__}", line {lineno_g+2}, in g\n'
435            '    return g(count-1)\n'
436            f'  File "{__file__}", line {lineno_g+2}, in g\n'
437            '    return g(count-1)\n'
438            f'  File "{__file__}", line {lineno_g+3}, in g\n'
439            '    raise ValueError\n'
440            'ValueError\n'
441        )
442        tb_line = (
443            'Traceback (most recent call last):\n'
444            f'  File "{__file__}", line {lineno_g+71}, in _check_recursive_traceback_display\n'
445            '    g(traceback._RECURSIVE_CUTOFF)\n'
446        )
447        expected = (tb_line + result_g).splitlines()
448        actual = stderr_g.getvalue().splitlines()
449        self.assertEqual(actual, expected)
450
451        # Second, test just above the cutoff.
452        with captured_output("stderr") as stderr_g:
453            try:
454                g(traceback._RECURSIVE_CUTOFF + 1)
455            except ValueError as exc:
456                render_exc()
457            else:
458                self.fail("no error raised")
459        result_g = (
460            f'  File "{__file__}", line {lineno_g+2}, in g\n'
461            '    return g(count-1)\n'
462            f'  File "{__file__}", line {lineno_g+2}, in g\n'
463            '    return g(count-1)\n'
464            f'  File "{__file__}", line {lineno_g+2}, in g\n'
465            '    return g(count-1)\n'
466            '  [Previous line repeated 1 more time]\n'
467            f'  File "{__file__}", line {lineno_g+3}, in g\n'
468            '    raise ValueError\n'
469            'ValueError\n'
470        )
471        tb_line = (
472            'Traceback (most recent call last):\n'
473            f'  File "{__file__}", line {lineno_g+99}, in _check_recursive_traceback_display\n'
474            '    g(traceback._RECURSIVE_CUTOFF + 1)\n'
475        )
476        expected = (tb_line + result_g).splitlines()
477        actual = stderr_g.getvalue().splitlines()
478        self.assertEqual(actual, expected)
479
480    def test_recursive_traceback_python(self):
481        self._check_recursive_traceback_display(traceback.print_exc)
482
483    @cpython_only
484    def test_recursive_traceback_cpython_internal(self):
485        from _testcapi import exception_print
486        def render_exc():
487            exc_type, exc_value, exc_tb = sys.exc_info()
488            exception_print(exc_value)
489        self._check_recursive_traceback_display(render_exc)
490
491    def test_format_stack(self):
492        def fmt():
493            return traceback.format_stack()
494        result = fmt()
495        lineno = fmt.__code__.co_firstlineno
496        self.assertEqual(result[-2:], [
497            '  File "%s", line %d, in test_format_stack\n'
498            '    result = fmt()\n' % (__file__, lineno+2),
499            '  File "%s", line %d, in fmt\n'
500            '    return traceback.format_stack()\n' % (__file__, lineno+1),
501        ])
502
503    @cpython_only
504    def test_unhashable(self):
505        from _testcapi import exception_print
506
507        class UnhashableException(Exception):
508            def __eq__(self, other):
509                return True
510
511        ex1 = UnhashableException('ex1')
512        ex2 = UnhashableException('ex2')
513        try:
514            raise ex2 from ex1
515        except UnhashableException:
516            try:
517                raise ex1
518            except UnhashableException:
519                exc_type, exc_val, exc_tb = sys.exc_info()
520
521        with captured_output("stderr") as stderr_f:
522            exception_print(exc_val)
523
524        tb = stderr_f.getvalue().strip().splitlines()
525        self.assertEqual(11, len(tb))
526        self.assertEqual(context_message.strip(), tb[5])
527        self.assertIn('UnhashableException: ex2', tb[3])
528        self.assertIn('UnhashableException: ex1', tb[10])
529
530
531cause_message = (
532    "\nThe above exception was the direct cause "
533    "of the following exception:\n\n")
534
535context_message = (
536    "\nDuring handling of the above exception, "
537    "another exception occurred:\n\n")
538
539boundaries = re.compile(
540    '(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
541
542
543class BaseExceptionReportingTests:
544
545    def get_exception(self, exception_or_callable):
546        if isinstance(exception_or_callable, Exception):
547            return exception_or_callable
548        try:
549            exception_or_callable()
550        except Exception as e:
551            return e
552
553    def zero_div(self):
554        1/0 # In zero_div
555
556    def check_zero_div(self, msg):
557        lines = msg.splitlines()
558        self.assertTrue(lines[-3].startswith('  File'))
559        self.assertIn('1/0 # In zero_div', lines[-2])
560        self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1])
561
562    def test_simple(self):
563        try:
564            1/0 # Marker
565        except ZeroDivisionError as _:
566            e = _
567        lines = self.get_report(e).splitlines()
568        self.assertEqual(len(lines), 4)
569        self.assertTrue(lines[0].startswith('Traceback'))
570        self.assertTrue(lines[1].startswith('  File'))
571        self.assertIn('1/0 # Marker', lines[2])
572        self.assertTrue(lines[3].startswith('ZeroDivisionError'))
573
574    def test_cause(self):
575        def inner_raise():
576            try:
577                self.zero_div()
578            except ZeroDivisionError as e:
579                raise KeyError from e
580        def outer_raise():
581            inner_raise() # Marker
582        blocks = boundaries.split(self.get_report(outer_raise))
583        self.assertEqual(len(blocks), 3)
584        self.assertEqual(blocks[1], cause_message)
585        self.check_zero_div(blocks[0])
586        self.assertIn('inner_raise() # Marker', blocks[2])
587
588    def test_context(self):
589        def inner_raise():
590            try:
591                self.zero_div()
592            except ZeroDivisionError:
593                raise KeyError
594        def outer_raise():
595            inner_raise() # Marker
596        blocks = boundaries.split(self.get_report(outer_raise))
597        self.assertEqual(len(blocks), 3)
598        self.assertEqual(blocks[1], context_message)
599        self.check_zero_div(blocks[0])
600        self.assertIn('inner_raise() # Marker', blocks[2])
601
602    def test_context_suppression(self):
603        try:
604            try:
605                raise Exception
606            except:
607                raise ZeroDivisionError from None
608        except ZeroDivisionError as _:
609            e = _
610        lines = self.get_report(e).splitlines()
611        self.assertEqual(len(lines), 4)
612        self.assertTrue(lines[0].startswith('Traceback'))
613        self.assertTrue(lines[1].startswith('  File'))
614        self.assertIn('ZeroDivisionError from None', lines[2])
615        self.assertTrue(lines[3].startswith('ZeroDivisionError'))
616
617    def test_cause_and_context(self):
618        # When both a cause and a context are set, only the cause should be
619        # displayed and the context should be muted.
620        def inner_raise():
621            try:
622                self.zero_div()
623            except ZeroDivisionError as _e:
624                e = _e
625            try:
626                xyzzy
627            except NameError:
628                raise KeyError from e
629        def outer_raise():
630            inner_raise() # Marker
631        blocks = boundaries.split(self.get_report(outer_raise))
632        self.assertEqual(len(blocks), 3)
633        self.assertEqual(blocks[1], cause_message)
634        self.check_zero_div(blocks[0])
635        self.assertIn('inner_raise() # Marker', blocks[2])
636
637    def test_cause_recursive(self):
638        def inner_raise():
639            try:
640                try:
641                    self.zero_div()
642                except ZeroDivisionError as e:
643                    z = e
644                    raise KeyError from e
645            except KeyError as e:
646                raise z from e
647        def outer_raise():
648            inner_raise() # Marker
649        blocks = boundaries.split(self.get_report(outer_raise))
650        self.assertEqual(len(blocks), 3)
651        self.assertEqual(blocks[1], cause_message)
652        # The first block is the KeyError raised from the ZeroDivisionError
653        self.assertIn('raise KeyError from e', blocks[0])
654        self.assertNotIn('1/0', blocks[0])
655        # The second block (apart from the boundary) is the ZeroDivisionError
656        # re-raised from the KeyError
657        self.assertIn('inner_raise() # Marker', blocks[2])
658        self.check_zero_div(blocks[2])
659
660    def test_syntax_error_offset_at_eol(self):
661        # See #10186.
662        def e():
663            raise SyntaxError('', ('', 0, 5, 'hello'))
664        msg = self.get_report(e).splitlines()
665        self.assertEqual(msg[-2], "        ^")
666        def e():
667            exec("x = 5 | 4 |")
668        msg = self.get_report(e).splitlines()
669        self.assertEqual(msg[-2], '              ^')
670
671    def test_message_none(self):
672        # A message that looks like "None" should not be treated specially
673        err = self.get_report(Exception(None))
674        self.assertIn('Exception: None\n', err)
675        err = self.get_report(Exception('None'))
676        self.assertIn('Exception: None\n', err)
677        err = self.get_report(Exception())
678        self.assertIn('Exception\n', err)
679        err = self.get_report(Exception(''))
680        self.assertIn('Exception\n', err)
681
682
683class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
684    #
685    # This checks reporting through the 'traceback' module, with both
686    # format_exception() and print_exception().
687    #
688
689    def get_report(self, e):
690        e = self.get_exception(e)
691        s = ''.join(
692            traceback.format_exception(type(e), e, e.__traceback__))
693        with captured_output("stderr") as sio:
694            traceback.print_exception(type(e), e, e.__traceback__)
695        self.assertEqual(sio.getvalue(), s)
696        return s
697
698
699class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
700    #
701    # This checks built-in reporting by the interpreter.
702    #
703
704    @cpython_only
705    def get_report(self, e):
706        from _testcapi import exception_print
707        e = self.get_exception(e)
708        with captured_output("stderr") as s:
709            exception_print(e)
710        return s.getvalue()
711
712
713class LimitTests(unittest.TestCase):
714
715    ''' Tests for limit argument.
716        It's enough to test extact_tb, extract_stack and format_exception '''
717
718    def last_raises1(self):
719        raise Exception('Last raised')
720
721    def last_raises2(self):
722        self.last_raises1()
723
724    def last_raises3(self):
725        self.last_raises2()
726
727    def last_raises4(self):
728        self.last_raises3()
729
730    def last_raises5(self):
731        self.last_raises4()
732
733    def last_returns_frame1(self):
734        return sys._getframe()
735
736    def last_returns_frame2(self):
737        return self.last_returns_frame1()
738
739    def last_returns_frame3(self):
740        return self.last_returns_frame2()
741
742    def last_returns_frame4(self):
743        return self.last_returns_frame3()
744
745    def last_returns_frame5(self):
746        return self.last_returns_frame4()
747
748    def test_extract_stack(self):
749        frame = self.last_returns_frame5()
750        def extract(**kwargs):
751            return traceback.extract_stack(frame, **kwargs)
752        def assertEqualExcept(actual, expected, ignore):
753            self.assertEqual(actual[:ignore], expected[:ignore])
754            self.assertEqual(actual[ignore+1:], expected[ignore+1:])
755            self.assertEqual(len(actual), len(expected))
756
757        with support.swap_attr(sys, 'tracebacklimit', 1000):
758            nolim = extract()
759            self.assertGreater(len(nolim), 5)
760            self.assertEqual(extract(limit=2), nolim[-2:])
761            assertEqualExcept(extract(limit=100), nolim[-100:], -5-1)
762            self.assertEqual(extract(limit=-2), nolim[:2])
763            assertEqualExcept(extract(limit=-100), nolim[:100], len(nolim)-5-1)
764            self.assertEqual(extract(limit=0), [])
765            del sys.tracebacklimit
766            assertEqualExcept(extract(), nolim, -5-1)
767            sys.tracebacklimit = 2
768            self.assertEqual(extract(), nolim[-2:])
769            self.assertEqual(extract(limit=3), nolim[-3:])
770            self.assertEqual(extract(limit=-3), nolim[:3])
771            sys.tracebacklimit = 0
772            self.assertEqual(extract(), [])
773            sys.tracebacklimit = -1
774            self.assertEqual(extract(), [])
775
776    def test_extract_tb(self):
777        try:
778            self.last_raises5()
779        except Exception:
780            exc_type, exc_value, tb = sys.exc_info()
781        def extract(**kwargs):
782            return traceback.extract_tb(tb, **kwargs)
783
784        with support.swap_attr(sys, 'tracebacklimit', 1000):
785            nolim = extract()
786            self.assertEqual(len(nolim), 5+1)
787            self.assertEqual(extract(limit=2), nolim[:2])
788            self.assertEqual(extract(limit=10), nolim)
789            self.assertEqual(extract(limit=-2), nolim[-2:])
790            self.assertEqual(extract(limit=-10), nolim)
791            self.assertEqual(extract(limit=0), [])
792            del sys.tracebacklimit
793            self.assertEqual(extract(), nolim)
794            sys.tracebacklimit = 2
795            self.assertEqual(extract(), nolim[:2])
796            self.assertEqual(extract(limit=3), nolim[:3])
797            self.assertEqual(extract(limit=-3), nolim[-3:])
798            sys.tracebacklimit = 0
799            self.assertEqual(extract(), [])
800            sys.tracebacklimit = -1
801            self.assertEqual(extract(), [])
802
803    def test_format_exception(self):
804        try:
805            self.last_raises5()
806        except Exception:
807            exc_type, exc_value, tb = sys.exc_info()
808        # [1:-1] to exclude "Traceback (...)" header and
809        # exception type and value
810        def extract(**kwargs):
811            return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
812
813        with support.swap_attr(sys, 'tracebacklimit', 1000):
814            nolim = extract()
815            self.assertEqual(len(nolim), 5+1)
816            self.assertEqual(extract(limit=2), nolim[:2])
817            self.assertEqual(extract(limit=10), nolim)
818            self.assertEqual(extract(limit=-2), nolim[-2:])
819            self.assertEqual(extract(limit=-10), nolim)
820            self.assertEqual(extract(limit=0), [])
821            del sys.tracebacklimit
822            self.assertEqual(extract(), nolim)
823            sys.tracebacklimit = 2
824            self.assertEqual(extract(), nolim[:2])
825            self.assertEqual(extract(limit=3), nolim[:3])
826            self.assertEqual(extract(limit=-3), nolim[-3:])
827            sys.tracebacklimit = 0
828            self.assertEqual(extract(), [])
829            sys.tracebacklimit = -1
830            self.assertEqual(extract(), [])
831
832
833class MiscTracebackCases(unittest.TestCase):
834    #
835    # Check non-printing functions in traceback module
836    #
837
838    def test_clear(self):
839        def outer():
840            middle()
841        def middle():
842            inner()
843        def inner():
844            i = 1
845            1/0
846
847        try:
848            outer()
849        except:
850            type_, value, tb = sys.exc_info()
851
852        # Initial assertion: there's one local in the inner frame.
853        inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
854        self.assertEqual(len(inner_frame.f_locals), 1)
855
856        # Clear traceback frames
857        traceback.clear_frames(tb)
858
859        # Local variable dict should now be empty.
860        self.assertEqual(len(inner_frame.f_locals), 0)
861
862    def test_extract_stack(self):
863        def extract():
864            return traceback.extract_stack()
865        result = extract()
866        lineno = extract.__code__.co_firstlineno
867        self.assertEqual(result[-2:], [
868            (__file__, lineno+2, 'test_extract_stack', 'result = extract()'),
869            (__file__, lineno+1, 'extract', 'return traceback.extract_stack()'),
870            ])
871
872
873class TestFrame(unittest.TestCase):
874
875    def test_basics(self):
876        linecache.clearcache()
877        linecache.lazycache("f", globals())
878        f = traceback.FrameSummary("f", 1, "dummy")
879        self.assertEqual(f,
880            ("f", 1, "dummy", '"""Test cases for traceback module"""'))
881        self.assertEqual(tuple(f),
882            ("f", 1, "dummy", '"""Test cases for traceback module"""'))
883        self.assertEqual(f, traceback.FrameSummary("f", 1, "dummy"))
884        self.assertEqual(f, tuple(f))
885        # Since tuple.__eq__ doesn't support FrameSummary, the equality
886        # operator fallbacks to FrameSummary.__eq__.
887        self.assertEqual(tuple(f), f)
888        self.assertIsNone(f.locals)
889
890    def test_lazy_lines(self):
891        linecache.clearcache()
892        f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
893        self.assertEqual(None, f._line)
894        linecache.lazycache("f", globals())
895        self.assertEqual(
896            '"""Test cases for traceback module"""',
897            f.line)
898
899    def test_explicit_line(self):
900        f = traceback.FrameSummary("f", 1, "dummy", line="line")
901        self.assertEqual("line", f.line)
902
903
904class TestStack(unittest.TestCase):
905
906    def test_walk_stack(self):
907        def deeper():
908            return list(traceback.walk_stack(None))
909        s1 = list(traceback.walk_stack(None))
910        s2 = deeper()
911        self.assertEqual(len(s2) - len(s1), 1)
912        self.assertEqual(s2[1:], s1)
913
914    def test_walk_tb(self):
915        try:
916            1/0
917        except Exception:
918            _, _, tb = sys.exc_info()
919        s = list(traceback.walk_tb(tb))
920        self.assertEqual(len(s), 1)
921
922    def test_extract_stack(self):
923        s = traceback.StackSummary.extract(traceback.walk_stack(None))
924        self.assertIsInstance(s, traceback.StackSummary)
925
926    def test_extract_stack_limit(self):
927        s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
928        self.assertEqual(len(s), 5)
929
930    def test_extract_stack_lookup_lines(self):
931        linecache.clearcache()
932        linecache.updatecache('/foo.py', globals())
933        c = test_code('/foo.py', 'method')
934        f = test_frame(c, None, None)
935        s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
936        linecache.clearcache()
937        self.assertEqual(s[0].line, "import sys")
938
939    def test_extract_stackup_deferred_lookup_lines(self):
940        linecache.clearcache()
941        c = test_code('/foo.py', 'method')
942        f = test_frame(c, None, None)
943        s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
944        self.assertEqual({}, linecache.cache)
945        linecache.updatecache('/foo.py', globals())
946        self.assertEqual(s[0].line, "import sys")
947
948    def test_from_list(self):
949        s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
950        self.assertEqual(
951            ['  File "foo.py", line 1, in fred\n    line\n'],
952            s.format())
953
954    def test_from_list_edited_stack(self):
955        s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
956        s[0] = ('foo.py', 2, 'fred', 'line')
957        s2 = traceback.StackSummary.from_list(s)
958        self.assertEqual(
959            ['  File "foo.py", line 2, in fred\n    line\n'],
960            s2.format())
961
962    def test_format_smoke(self):
963        # For detailed tests see the format_list tests, which consume the same
964        # code.
965        s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
966        self.assertEqual(
967            ['  File "foo.py", line 1, in fred\n    line\n'],
968            s.format())
969
970    def test_locals(self):
971        linecache.updatecache('/foo.py', globals())
972        c = test_code('/foo.py', 'method')
973        f = test_frame(c, globals(), {'something': 1})
974        s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True)
975        self.assertEqual(s[0].locals, {'something': '1'})
976
977    def test_no_locals(self):
978        linecache.updatecache('/foo.py', globals())
979        c = test_code('/foo.py', 'method')
980        f = test_frame(c, globals(), {'something': 1})
981        s = traceback.StackSummary.extract(iter([(f, 6)]))
982        self.assertEqual(s[0].locals, None)
983
984    def test_format_locals(self):
985        def some_inner(k, v):
986            a = 1
987            b = 2
988            return traceback.StackSummary.extract(
989                traceback.walk_stack(None), capture_locals=True, limit=1)
990        s = some_inner(3, 4)
991        self.assertEqual(
992            ['  File "%s", line %d, in some_inner\n'
993             '    traceback.walk_stack(None), capture_locals=True, limit=1)\n'
994             '    a = 1\n'
995             '    b = 2\n'
996             '    k = 3\n'
997             '    v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 4)
998            ], s.format())
999
1000class TestTracebackException(unittest.TestCase):
1001
1002    def test_smoke(self):
1003        try:
1004            1/0
1005        except Exception:
1006            exc_info = sys.exc_info()
1007            exc = traceback.TracebackException(*exc_info)
1008            expected_stack = traceback.StackSummary.extract(
1009                traceback.walk_tb(exc_info[2]))
1010        self.assertEqual(None, exc.__cause__)
1011        self.assertEqual(None, exc.__context__)
1012        self.assertEqual(False, exc.__suppress_context__)
1013        self.assertEqual(expected_stack, exc.stack)
1014        self.assertEqual(exc_info[0], exc.exc_type)
1015        self.assertEqual(str(exc_info[1]), str(exc))
1016
1017    def test_from_exception(self):
1018        # Check all the parameters are accepted.
1019        def foo():
1020            1/0
1021        try:
1022            foo()
1023        except Exception as e:
1024            exc_info = sys.exc_info()
1025            self.expected_stack = traceback.StackSummary.extract(
1026                traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
1027                capture_locals=True)
1028            self.exc = traceback.TracebackException.from_exception(
1029                e, limit=1, lookup_lines=False, capture_locals=True)
1030        expected_stack = self.expected_stack
1031        exc = self.exc
1032        self.assertEqual(None, exc.__cause__)
1033        self.assertEqual(None, exc.__context__)
1034        self.assertEqual(False, exc.__suppress_context__)
1035        self.assertEqual(expected_stack, exc.stack)
1036        self.assertEqual(exc_info[0], exc.exc_type)
1037        self.assertEqual(str(exc_info[1]), str(exc))
1038
1039    def test_cause(self):
1040        try:
1041            try:
1042                1/0
1043            finally:
1044                exc_info_context = sys.exc_info()
1045                exc_context = traceback.TracebackException(*exc_info_context)
1046                cause = Exception("cause")
1047                raise Exception("uh oh") from cause
1048        except Exception:
1049            exc_info = sys.exc_info()
1050            exc = traceback.TracebackException(*exc_info)
1051            expected_stack = traceback.StackSummary.extract(
1052                traceback.walk_tb(exc_info[2]))
1053        exc_cause = traceback.TracebackException(Exception, cause, None)
1054        self.assertEqual(exc_cause, exc.__cause__)
1055        self.assertEqual(exc_context, exc.__context__)
1056        self.assertEqual(True, exc.__suppress_context__)
1057        self.assertEqual(expected_stack, exc.stack)
1058        self.assertEqual(exc_info[0], exc.exc_type)
1059        self.assertEqual(str(exc_info[1]), str(exc))
1060
1061    def test_context(self):
1062        try:
1063            try:
1064                1/0
1065            finally:
1066                exc_info_context = sys.exc_info()
1067                exc_context = traceback.TracebackException(*exc_info_context)
1068                raise Exception("uh oh")
1069        except Exception:
1070            exc_info = sys.exc_info()
1071            exc = traceback.TracebackException(*exc_info)
1072            expected_stack = traceback.StackSummary.extract(
1073                traceback.walk_tb(exc_info[2]))
1074        self.assertEqual(None, exc.__cause__)
1075        self.assertEqual(exc_context, exc.__context__)
1076        self.assertEqual(False, exc.__suppress_context__)
1077        self.assertEqual(expected_stack, exc.stack)
1078        self.assertEqual(exc_info[0], exc.exc_type)
1079        self.assertEqual(str(exc_info[1]), str(exc))
1080
1081    def test_unhashable(self):
1082        class UnhashableException(Exception):
1083            def __eq__(self, other):
1084                return True
1085
1086        ex1 = UnhashableException('ex1')
1087        ex2 = UnhashableException('ex2')
1088        try:
1089            raise ex2 from ex1
1090        except UnhashableException:
1091            try:
1092                raise ex1
1093            except UnhashableException:
1094                exc_info = sys.exc_info()
1095        exc = traceback.TracebackException(*exc_info)
1096        formatted = list(exc.format())
1097        self.assertIn('UnhashableException: ex2\n', formatted[2])
1098        self.assertIn('UnhashableException: ex1\n', formatted[6])
1099
1100    def test_limit(self):
1101        def recurse(n):
1102            if n:
1103                recurse(n-1)
1104            else:
1105                1/0
1106        try:
1107            recurse(10)
1108        except Exception:
1109            exc_info = sys.exc_info()
1110            exc = traceback.TracebackException(*exc_info, limit=5)
1111            expected_stack = traceback.StackSummary.extract(
1112                traceback.walk_tb(exc_info[2]), limit=5)
1113        self.assertEqual(expected_stack, exc.stack)
1114
1115    def test_lookup_lines(self):
1116        linecache.clearcache()
1117        e = Exception("uh oh")
1118        c = test_code('/foo.py', 'method')
1119        f = test_frame(c, None, None)
1120        tb = test_tb(f, 6, None)
1121        exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
1122        self.assertEqual({}, linecache.cache)
1123        linecache.updatecache('/foo.py', globals())
1124        self.assertEqual(exc.stack[0].line, "import sys")
1125
1126    def test_locals(self):
1127        linecache.updatecache('/foo.py', globals())
1128        e = Exception("uh oh")
1129        c = test_code('/foo.py', 'method')
1130        f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
1131        tb = test_tb(f, 6, None)
1132        exc = traceback.TracebackException(
1133            Exception, e, tb, capture_locals=True)
1134        self.assertEqual(
1135            exc.stack[0].locals, {'something': '1', 'other': "'string'"})
1136
1137    def test_no_locals(self):
1138        linecache.updatecache('/foo.py', globals())
1139        e = Exception("uh oh")
1140        c = test_code('/foo.py', 'method')
1141        f = test_frame(c, globals(), {'something': 1})
1142        tb = test_tb(f, 6, None)
1143        exc = traceback.TracebackException(Exception, e, tb)
1144        self.assertEqual(exc.stack[0].locals, None)
1145
1146    def test_traceback_header(self):
1147        # do not print a traceback header if exc_traceback is None
1148        # see issue #24695
1149        exc = traceback.TracebackException(Exception, Exception("haven"), None)
1150        self.assertEqual(list(exc.format()), ["Exception: haven\n"])
1151
1152
1153class MiscTest(unittest.TestCase):
1154
1155    def test_all(self):
1156        expected = set()
1157        blacklist = {'print_list'}
1158        for name in dir(traceback):
1159            if name.startswith('_') or name in blacklist:
1160                continue
1161            module_object = getattr(traceback, name)
1162            if getattr(module_object, '__module__', None) == 'traceback':
1163                expected.add(name)
1164        self.assertCountEqual(traceback.__all__, expected)
1165
1166
1167if __name__ == "__main__":
1168    unittest.main()
1169