diff options
author | R David Murray <rdmurray@bitdance.com> | 2014-09-29 15:25:00 (GMT) |
---|---|---|
committer | R David Murray <rdmurray@bitdance.com> | 2014-09-29 15:25:00 (GMT) |
commit | c31e6227f94c2bb0290336c739873173672a8991 (patch) | |
tree | d0ae5b35cf326fa9605608f0860d6789bc115d78 | |
parent | 4d75a01798cf0ff112335804382bd4bf50f7bf78 (diff) | |
download | cpython-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.rst | 3 | ||||
-rw-r--r-- | Doc/whatsnew/3.5.rst | 7 | ||||
-rw-r--r-- | Lib/code.py | 34 | ||||
-rw-r--r-- | Lib/test/test_code_module.py | 35 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
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) @@ -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. |