Index: Doc/library/traceback.rst =================================================================== --- Doc/library/traceback.rst (revision 85325) +++ Doc/library/traceback.rst (working copy) @@ -28,7 +28,7 @@ object to receive the output. -.. function:: print_exception(type, value, traceback, limit=None, file=None, chain=True) +.. function:: print_exception(type, value, traceback, limit=None, file=None, chain=True, allframes=False) Print exception information and up to *limit* stack trace entries from *traceback* to *file*. This differs from :func:`print_tb` in the following @@ -40,11 +40,16 @@ * if *type* is :exc:`SyntaxError` and *value* has the appropriate format, it prints the line where the syntax error occurred with a caret indicating the approximate position of the error. + * if *allframes* is true, the lines from the traceback are prefixed by the + lines from :func:`extract_stack` starting from the frame above the first + line in the traceback, thereby producing a traceback report that starts + from the top of the interpreter stack instead of from wherever the + exception was captured. If *chain* is true (the default), then chained exceptions (the :attr:`__cause__` or :attr:`__context__` attributes of the exception) will be printed as well, like the interpreter itself does when printing an unhandled - exception. + exception. *fullstack* applies to the chained exceptions as well. .. function:: print_exc(limit=None, file=None, chain=True) @@ -105,13 +110,13 @@ occurred is the always last string in the list. -.. function:: format_exception(type, value, tb, limit=None, chain=True) +.. function:: format_exception(type, value, tb, limit=None, chain=True, allframes=False) Format a stack trace and the exception information. The arguments have the same meaning as the corresponding arguments to :func:`print_exception`. The return value is a list of strings, each ending in a newline and some containing internal newlines. When these lines are concatenated and printed, exactly the - same text is printed as does :func:`print_exception`. + same text is printed as does the corresponding :func:`print_exception` call. .. function:: format_exc(limit=None, chain=True) Index: Lib/traceback.py =================================================================== --- Lib/traceback.py (revision 85325) +++ Lib/traceback.py (working copy) @@ -135,7 +135,8 @@ yield x -def print_exception(etype, value, tb, limit=None, file=None, chain=True): +def print_exception(etype, value, tb, limit=None, file=None, chain=True, + allframes=False): """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. This differs from print_tb() in the following ways: (1) if @@ -144,7 +145,10 @@ stack trace; (3) if type is SyntaxError and value has the appropriate format, it prints the line where the syntax error occurred with a caret on the next line indicating the approximate - position of the error. + position of the error (4) if allframes is True it prefixes the + traceback with the print_stack starting from the frame above the + first frame in the traceback, thus giving a 'full' traceback from + the top of the interpreter stack. """ if file is None: file = sys.stderr @@ -158,19 +162,22 @@ continue if tb: _print(file, 'Traceback (most recent call last):') + if allframes and tb.tb_frame.f_back: + print_stack(tb.tb_frame.f_back, file=file) print_tb(tb, limit, file) lines = format_exception_only(type(value), value) for line in lines: _print(file, line, '') -def format_exception(etype, value, tb, limit=None, chain=True): +def format_exception(etype, value, tb, limit=None, chain=True, + allframes=False): """Format a stack trace and the exception information. The arguments have the same meaning as the corresponding arguments to print_exception(). The return value is a list of strings, each ending in a newline and some containing internal newlines. When these lines are concatenated and printed, exactly the same text is - printed as does print_exception(). + printed as does the equivalent call to print_exception(). """ list = [] if chain: @@ -183,6 +190,8 @@ continue if tb: list.append('Traceback (most recent call last):\n') + if allframes and tb.tb_frame.f_back: + list.extend(format_stack(tb.tb_frame.f_back)) list.extend(format_tb(tb, limit)) list.extend(format_exception_only(type(value), value)) return list Index: Lib/test/test_traceback.py =================================================================== --- Lib/test/test_traceback.py (revision 85325) +++ Lib/test/test_traceback.py (working copy) @@ -5,6 +5,8 @@ import sys import unittest import re +import subprocess +import textwrap from test.support import run_unittest, Error, captured_output from test.support import TESTFN, unlink @@ -148,6 +150,124 @@ text, charset, 5) +class BaseTracebackContentTests: + + maxDiff = None + testfn = 'testmod.py' + + def _do_test(self, program, exc_text): + with open(self.testfn, 'w') as testmod: + testmod.writelines(program.format( + exception_action=self.exception_action)) + p = subprocess.Popen([sys.executable, 'testmod.py'], + stderr=subprocess.PIPE) + self.assertEqual(b''.join(p.stderr.readlines()[:-1]).decode('ASCII'), + exc_text.format( + exception_action=self.exception_action)) + + def test_full_traceback_is_full(self): + self._do_test( + textwrap.dedent("""\ + import sys + import traceback + + def foo(): + try: + raise Exception + except Exception: + {exception_action} + + def upper(): + foo() + + upper() + """), + textwrap.dedent("""\ + Traceback (most recent call last): + File "testmod.py", line 13, in + upper() + File "testmod.py", line 11, in upper + foo() + File "testmod.py", line 6, in foo + raise Exception + Exception + """)) + + def test_top_level_full_traceback_is_full(self): + self._do_test( + textwrap.dedent("""\ + import sys + import traceback + + try: + raise Exception + except Exception: + {exception_action} + """), + textwrap.dedent("""\ + Traceback (most recent call last): + File "testmod.py", line 5, in + raise Exception + Exception + """)) + + def test_full_traceback_chained_is_full(self): + self._do_test( + textwrap.dedent("""\ + import sys + import traceback + + def foo(): + try: + bar() + except Exception: + 1/0 + + def bar(): + raise Exception + + try: + foo() + except: + {exception_action} + """), + textwrap.dedent("""\ + Traceback (most recent call last): + File "testmod.py", line 16, in + {exception_action} + File "testmod.py", line 6, in foo + bar() + File "testmod.py", line 11, in bar + raise Exception + Exception + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "testmod.py", line 14, in + foo() + File "testmod.py", line 8, in foo + 1/0 + ZeroDivisionError: division by zero + """)) + + +class PrintTracebackContentTests(BaseTracebackContentTests, + unittest.TestCase): + + exception_action = ( + "traceback.print_exception(*sys.exc_info(), " + "allframes=True)") + + +class FormatTracebackContentTests(BaseTracebackContentTests, + unittest.TestCase): + + _ = "traceback.format_exception(*sys.exc_info(), allframes=True)" + exception_action = r"print(''.join({}), end='', file=sys.stderr)".format(_) + del _ + + class TracebackFormatTests(unittest.TestCase): def test_traceback_format(self):