diff -r 2203f377aab1 Doc/library/traceback.rst --- a/Doc/library/traceback.rst Sat Jul 24 16:15:19 2010 +0200 +++ b/Doc/library/traceback.rst Tue Aug 03 18:24:48 2010 -0400 @@ -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, fullstack=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 *fullstack* 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) diff -r 2203f377aab1 Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py Sat Jul 24 16:15:19 2010 +0200 +++ b/Lib/test/test_traceback.py Tue Aug 03 18:24:48 2010 -0400 @@ -5,6 +5,8 @@ import sys import unittest import re +import subprocess +import textwrap from test.support import run_unittest, is_jython, Error, captured_output from test.support import TESTFN, unlink @@ -155,6 +157,109 @@ text, charset, 5) +class TracebackContentTests(unittest.TestCase): + + maxDiff = None + + def test_full_traceback_is_full(self): + with open('testmod.py', 'w') as testmod: + testmod.writelines(textwrap.dedent("""\ + import sys + import traceback + + def foo(): + try: + raise Exception + except Exception: + traceback.print_exception(*sys.exc_info(), + fullstack=True) + def upper(): + foo() + + upper() + """)) + p = subprocess.Popen([sys.executable, 'testmod.py'], + stderr=subprocess.PIPE) + self.assertEqual(b''.join(p.stderr.readlines()[:-1]).decode('ASCII'), + 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_full_traceback_is_full(self): + with open('testmod.py', 'w') as testmod: + testmod.writelines(textwrap.dedent("""\ + import sys + import traceback + + try: + raise Exception + except Exception: + traceback.print_exception(*sys.exc_info(), + fullstack=True) + """)) + p = subprocess.Popen([sys.executable, 'testmod.py'], + stderr=subprocess.PIPE) + self.assertEqual(b''.join(p.stderr.readlines()[:-1]).decode('ASCII'), + textwrap.dedent("""\ + Traceback (most recent call last): + File "testmod.py", line 5, in + raise Exception + Exception + """)) + + def test_full_traceback_chained_is_full(self): + with open('testmod.py', 'w') as testmod: + testmod.writelines(textwrap.dedent("""\ + import sys + import traceback + + def foo(): + try: + bar() + except Exception: + 1/0 + + def bar(): + raise Exception + + try: + foo() + except: + traceback.print_exception(*sys.exc_info(), + fullstack=True) + + """)) + p = subprocess.Popen([sys.executable, 'testmod.py'], + stderr=subprocess.PIPE) + self.assertEqual(b''.join(p.stderr.readlines()[:-1]).decode('ASCII'), + textwrap.dedent("""\ + Traceback (most recent call last): + File "testmod.py", line 17, in + fullstack=True) + 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 TracebackFormatTests(unittest.TestCase): def test_traceback_format(self): diff -r 2203f377aab1 Lib/traceback.py --- a/Lib/traceback.py Sat Jul 24 16:15:19 2010 +0200 +++ b/Lib/traceback.py Tue Aug 03 18:24:48 2010 -0400 @@ -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, + fullstack=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 fullstack 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,6 +162,8 @@ continue if tb: _print(file, 'Traceback (most recent call last):') + if fullstack 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: