diff options
Diffstat (limited to 'Lib/test/test_traceback.py')
-rw-r--r-- | Lib/test/test_traceback.py | 284 |
1 files changed, 282 insertions, 2 deletions
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index c29556354ef..d9b73c133fc 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1,15 +1,24 @@ """Test cases for traceback module""" +from collections import namedtuple from io import StringIO +import linecache import sys import unittest import re from test.support import run_unittest, Error, captured_output from test.support import TESTFN, unlink, cpython_only +from test.script_helper import assert_python_ok +import textwrap import traceback +test_code = namedtuple('code', ['co_filename', 'co_name']) +test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals']) +test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next']) + + class SyntaxTracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. @@ -92,9 +101,9 @@ class SyntaxTracebackCases(unittest.TestCase): self.assertEqual(len(err), 1) str_value = '<unprintable %s object>' % X.__name__ if X.__module__ in ('__main__', 'builtins'): - str_name = X.__name__ + str_name = X.__qualname__ else: - str_name = '.'.join([X.__module__, X.__name__]) + str_name = '.'.join([X.__module__, X.__qualname__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) def test_without_exception(self): @@ -169,6 +178,37 @@ class SyntaxTracebackCases(unittest.TestCase): # Issue #18960: coding spec should has no effect do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) + def test_print_traceback_at_exit(self): + # Issue #22599: Ensure that it is possible to use the traceback module + # to display an exception at Python exit + code = textwrap.dedent(""" + import sys + import traceback + + class PrintExceptionAtExit(object): + def __init__(self): + try: + x = 1 / 0 + except Exception: + self.exc_info = sys.exc_info() + # self.exc_info[1] (traceback) contains frames: + # explicitly clear the reference to self in the current + # frame to break a reference cycle + self = None + + def __del__(self): + traceback.print_exception(*self.exc_info) + + # Keep a reference in the module namespace to call the destructor + # when the module is unloaded + obj = PrintExceptionAtExit() + """) + rc, stdout, stderr = assert_python_ok('-c', code) + expected = [b'Traceback (most recent call last):', + b' File "<string>", line 8, in __init__', + b'ZeroDivisionError: division by zero'] + self.assertEqual(stderr.splitlines(), expected) + class TracebackFormatTests(unittest.TestCase): @@ -444,6 +484,246 @@ class MiscTracebackCases(unittest.TestCase): self.assertEqual(len(inner_frame.f_locals), 0) +class TestFrame(unittest.TestCase): + + def test_basics(self): + linecache.clearcache() + linecache.lazycache("f", globals()) + f = traceback.FrameSummary("f", 1, "dummy") + self.assertEqual( + ("f", 1, "dummy", '"""Test cases for traceback module"""'), + tuple(f)) + self.assertEqual(None, f.locals) + + def test_lazy_lines(self): + linecache.clearcache() + f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False) + self.assertEqual(None, f._line) + linecache.lazycache("f", globals()) + self.assertEqual( + '"""Test cases for traceback module"""', + f.line) + + def test_explicit_line(self): + f = traceback.FrameSummary("f", 1, "dummy", line="line") + self.assertEqual("line", f.line) + + +class TestStack(unittest.TestCase): + + def test_walk_stack(self): + s = list(traceback.walk_stack(None)) + self.assertGreater(len(s), 10) + + def test_walk_tb(self): + try: + 1/0 + except Exception: + _, _, tb = sys.exc_info() + s = list(traceback.walk_tb(tb)) + self.assertEqual(len(s), 1) + + def test_extract_stack(self): + s = traceback.StackSummary.extract(traceback.walk_stack(None)) + self.assertIsInstance(s, traceback.StackSummary) + + def test_extract_stack_limit(self): + s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5) + self.assertEqual(len(s), 5) + + def test_extract_stack_lookup_lines(self): + linecache.clearcache() + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, None, None) + s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True) + linecache.clearcache() + self.assertEqual(s[0].line, "import sys") + + def test_extract_stackup_deferred_lookup_lines(self): + linecache.clearcache() + c = test_code('/foo.py', 'method') + f = test_frame(c, None, None) + s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False) + self.assertEqual({}, linecache.cache) + linecache.updatecache('/foo.py', globals()) + self.assertEqual(s[0].line, "import sys") + + def test_from_list(self): + s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) + self.assertEqual( + [' File "foo.py", line 1, in fred\n line\n'], + s.format()) + + def test_format_smoke(self): + # For detailed tests see the format_list tests, which consume the same + # code. + s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')]) + self.assertEqual( + [' File "foo.py", line 1, in fred\n line\n'], + s.format()) + + def test_locals(self): + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + s = traceback.StackSummary.extract(iter([(f, 6)]), capture_locals=True) + self.assertEqual(s[0].locals, {'something': '1'}) + + def test_no_locals(self): + linecache.updatecache('/foo.py', globals()) + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + s = traceback.StackSummary.extract(iter([(f, 6)])) + self.assertEqual(s[0].locals, None) + + def test_format_locals(self): + def some_inner(k, v): + a = 1 + b = 2 + return traceback.StackSummary.extract( + traceback.walk_stack(None), capture_locals=True, limit=1) + s = some_inner(3, 4) + self.assertEqual( + [' File "' + __file__ + '", line 585, ' + 'in some_inner\n' + ' traceback.walk_stack(None), capture_locals=True, limit=1)\n' + ' a = 1\n' + ' b = 2\n' + ' k = 3\n' + ' v = 4\n' + ], s.format()) + + + +class TestTracebackException(unittest.TestCase): + + def test_smoke(self): + try: + 1/0 + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + self.assertEqual(None, exc.__cause__) + self.assertEqual(None, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_from_exception(self): + # Check all the parameters are accepted. + def foo(): + 1/0 + try: + foo() + except Exception as e: + exc_info = sys.exc_info() + self.expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False, + capture_locals=True) + self.exc = traceback.TracebackException.from_exception( + e, limit=1, lookup_lines=False, capture_locals=True) + expected_stack = self.expected_stack + exc = self.exc + self.assertEqual(None, exc.__cause__) + self.assertEqual(None, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_cause(self): + try: + try: + 1/0 + finally: + exc_info_context = sys.exc_info() + exc_context = traceback.TracebackException(*exc_info_context) + cause = Exception("cause") + raise Exception("uh oh") from cause + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + exc_cause = traceback.TracebackException(Exception, cause, None) + self.assertEqual(exc_cause, exc.__cause__) + self.assertEqual(exc_context, exc.__context__) + self.assertEqual(True, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_context(self): + try: + try: + 1/0 + finally: + exc_info_context = sys.exc_info() + exc_context = traceback.TracebackException(*exc_info_context) + raise Exception("uh oh") + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2])) + self.assertEqual(None, exc.__cause__) + self.assertEqual(exc_context, exc.__context__) + self.assertEqual(False, exc.__suppress_context__) + self.assertEqual(expected_stack, exc.stack) + self.assertEqual(exc_info[0], exc.exc_type) + self.assertEqual(str(exc_info[1]), str(exc)) + + def test_limit(self): + def recurse(n): + if n: + recurse(n-1) + else: + 1/0 + try: + recurse(10) + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info, limit=5) + expected_stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_info[2]), limit=5) + self.assertEqual(expected_stack, exc.stack) + + def test_lookup_lines(self): + linecache.clearcache() + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, None, None) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) + self.assertEqual({}, linecache.cache) + linecache.updatecache('/foo.py', globals()) + self.assertEqual(exc.stack[0].line, "import sys") + + def test_locals(self): + linecache.updatecache('/foo.py', globals()) + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1, 'other': 'string'}) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException( + Exception, e, tb, capture_locals=True) + self.assertEqual( + exc.stack[0].locals, {'something': '1', 'other': "'string'"}) + + def test_no_locals(self): + linecache.updatecache('/foo.py', globals()) + e = Exception("uh oh") + c = test_code('/foo.py', 'method') + f = test_frame(c, globals(), {'something': 1}) + tb = test_tb(f, 6, None) + exc = traceback.TracebackException(Exception, e, tb) + self.assertEqual(exc.stack[0].locals, None) + + def test_main(): run_unittest(__name__) |