summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorR David Murray <rdmurray@bitdance.com>2014-09-29 15:25:00 (GMT)
committerR David Murray <rdmurray@bitdance.com>2014-09-29 15:25:00 (GMT)
commitc31e6227f94c2bb0290336c739873173672a8991 (patch)
treed0ae5b35cf326fa9605608f0860d6789bc115d78
parent4d75a01798cf0ff112335804382bd4bf50f7bf78 (diff)
downloadcpython-c31e6227f94c2bb0290336c739873173672a8991.zip
cpython-c31e6227f94c2bb0290336c739873173672a8991.tar.gz
cpython-c31e6227f94c2bb0290336c739873173672a8991.tar.bz2
#17442: Add chained traceback support to InteractiveInterpreter.
Patch by Claudiu Popa.
-rw-r--r--Doc/library/code.rst3
-rw-r--r--Doc/whatsnew/3.5.rst7
-rw-r--r--Lib/code.py34
-rw-r--r--Lib/test/test_code_module.py35
-rw-r--r--Misc/NEWS3
5 files changed, 70 insertions, 12 deletions
diff --git a/Doc/library/code.rst b/Doc/library/code.rst
index 99bdedc..275201c 100644
--- a/Doc/library/code.rst
+++ b/Doc/library/code.rst
@@ -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)
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 2e183a9..5c2be47 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -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
----------
diff --git a/Lib/code.py b/Lib/code.py
index 6186e04..86e1f03 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -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.
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index 7a80a80..9b17767 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -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)
diff --git a/Misc/NEWS b/Misc/NEWS
index 0215a5c..63942a9 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.