#17442: Add chained traceback support to InteractiveInterpreter.

Patch by Claudiu Popa.
This commit is contained in:
R David Murray 2014-09-29 11:25:00 -04:00
parent 4d75a01798
commit c31e6227f9
5 changed files with 70 additions and 12 deletions

View File

@ -114,6 +114,9 @@ Interactive Interpreter Objects
because it is within the interpreter object implementation. The output is
written by the :meth:`write` method.
.. versionchanged:: 3.5 The full chained traceback is displayed instead
of just the primary traceback.
.. method:: InteractiveInterpreter.write(data)

View File

@ -134,6 +134,13 @@ New Modules
Improved Modules
================
code
----
* The :func:`code.InteractiveInterpreter.showtraceback` method now prints
the full chained traceback, just like the interactive interpreter
(contributed by Claudiu.Popa in :issue:`17442`).
compileall
----------

View File

@ -137,25 +137,35 @@ class InteractiveInterpreter:
The output is written by self.write(), below.
"""
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
sys.last_traceback = last_tb
try:
type, value, tb = sys.exc_info()
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
tblist = traceback.extract_tb(tb)
del tblist[:1]
lines = traceback.format_list(tblist)
if lines:
lines.insert(0, "Traceback (most recent call last):\n")
lines.extend(traceback.format_exception_only(type, value))
lines = []
for value, tb in traceback._iter_chain(*ei[1:]):
if isinstance(value, str):
lines.append(value)
lines.append('\n')
continue
if tb:
tblist = traceback.extract_tb(tb)
if tb is last_tb:
# The last traceback includes the frame we
# exec'd in
del tblist[:1]
tblines = traceback.format_list(tblist)
if tblines:
lines.append("Traceback (most recent call last):\n")
lines.extend(tblines)
lines.extend(traceback.format_exception_only(type(value),
value))
finally:
tblist = tb = None
tblist = last_tb = ei = None
if sys.excepthook is sys.__excepthook__:
self.write(''.join(lines))
else:
# If someone has set sys.excepthook, we let that take precedence
# over self.write
sys.excepthook(type, value, tb)
sys.excepthook(type, value, last_tb)
def write(self, data):
"""Write a string.

View File

@ -1,6 +1,7 @@
"Test InteractiveConsole and InteractiveInterpreter from code module"
import sys
import unittest
from textwrap import dedent
from contextlib import ExitStack
from unittest import mock
from test import support
@ -78,6 +79,40 @@ class TestInteractiveConsole(unittest.TestCase):
self.console.interact(banner='')
self.assertEqual(len(self.stderr.method_calls), 1)
def test_cause_tb(self):
self.infunc.side_effect = ["raise ValueError('') from AttributeError",
EOFError('Finished')]
self.console.interact()
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
expected = dedent("""
AttributeError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError
""")
self.assertIn(expected, output)
def test_context_tb(self):
self.infunc.side_effect = ["try: ham\nexcept: eggs\n",
EOFError('Finished')]
self.console.interact()
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
expected = dedent("""
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'ham' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 2, in <module>
NameError: name 'eggs' is not defined
""")
self.assertIn(expected, output)
def test_main():
support.run_unittest(TestInteractiveConsole)

View File

@ -145,6 +145,9 @@ Core and Builtins
Library
-------
- Issue #17442: InteractiveInterpreter now displays the full chained traceback
in its showtraceback method, to match the built in interactive interpreter.
- Issue #10510: distutils register and upload methods now use HTML standards
compliant CRLF line endings.