#17442: Add chained traceback support to InteractiveInterpreter.
Patch by Claudiu Popa.
This commit is contained in:
parent
4d75a01798
commit
c31e6227f9
|
@ -114,6 +114,9 @@ Interactive Interpreter Objects
|
||||||
because it is within the interpreter object implementation. The output is
|
because it is within the interpreter object implementation. The output is
|
||||||
written by the :meth:`write` method.
|
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)
|
.. method:: InteractiveInterpreter.write(data)
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,13 @@ New Modules
|
||||||
Improved 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
|
compileall
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
34
Lib/code.py
34
Lib/code.py
|
@ -137,25 +137,35 @@ class InteractiveInterpreter:
|
||||||
The output is written by self.write(), below.
|
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:
|
try:
|
||||||
type, value, tb = sys.exc_info()
|
lines = []
|
||||||
sys.last_type = type
|
for value, tb in traceback._iter_chain(*ei[1:]):
|
||||||
sys.last_value = value
|
if isinstance(value, str):
|
||||||
sys.last_traceback = tb
|
lines.append(value)
|
||||||
tblist = traceback.extract_tb(tb)
|
lines.append('\n')
|
||||||
del tblist[:1]
|
continue
|
||||||
lines = traceback.format_list(tblist)
|
if tb:
|
||||||
if lines:
|
tblist = traceback.extract_tb(tb)
|
||||||
lines.insert(0, "Traceback (most recent call last):\n")
|
if tb is last_tb:
|
||||||
lines.extend(traceback.format_exception_only(type, value))
|
# 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:
|
finally:
|
||||||
tblist = tb = None
|
tblist = last_tb = ei = None
|
||||||
if sys.excepthook is sys.__excepthook__:
|
if sys.excepthook is sys.__excepthook__:
|
||||||
self.write(''.join(lines))
|
self.write(''.join(lines))
|
||||||
else:
|
else:
|
||||||
# If someone has set sys.excepthook, we let that take precedence
|
# If someone has set sys.excepthook, we let that take precedence
|
||||||
# over self.write
|
# over self.write
|
||||||
sys.excepthook(type, value, tb)
|
sys.excepthook(type, value, last_tb)
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
"""Write a string.
|
"""Write a string.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"Test InteractiveConsole and InteractiveInterpreter from code module"
|
"Test InteractiveConsole and InteractiveInterpreter from code module"
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
from textwrap import dedent
|
||||||
from contextlib import ExitStack
|
from contextlib import ExitStack
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -78,6 +79,40 @@ class TestInteractiveConsole(unittest.TestCase):
|
||||||
self.console.interact(banner='')
|
self.console.interact(banner='')
|
||||||
self.assertEqual(len(self.stderr.method_calls), 1)
|
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():
|
def test_main():
|
||||||
support.run_unittest(TestInteractiveConsole)
|
support.run_unittest(TestInteractiveConsole)
|
||||||
|
|
|
@ -145,6 +145,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #10510: distutils register and upload methods now use HTML standards
|
||||||
compliant CRLF line endings.
|
compliant CRLF line endings.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue