[Python-checkins] cpython: #17442: Add chained traceback support to InteractiveInterpreter.

r.david.murray python-checkins at python.org
Mon Sep 29 17:26:06 CEST 2014


https://hg.python.org/cpython/rev/2b212a8186e0
changeset:   92627:2b212a8186e0
user:        R David Murray <rdmurray at bitdance.com>
date:        Mon Sep 29 11:25:00 2014 -0400
summary:
  #17442: Add chained traceback support to InteractiveInterpreter.

Patch by Claudiu Popa.

files:
  Doc/library/code.rst         |   3 ++
  Doc/whatsnew/3.5.rst         |   7 ++++
  Lib/code.py                  |  34 +++++++++++++++--------
  Lib/test/test_code_module.py |  35 ++++++++++++++++++++++++
  Misc/NEWS                    |   3 ++
  5 files changed, 70 insertions(+), 12 deletions(-)


diff --git a/Doc/library/code.rst b/Doc/library/code.rst
--- a/Doc/library/code.rst
+++ b/Doc/library/code.rst
@@ -114,6 +114,9 @@
    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
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -134,6 +134,13 @@
 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
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -137,25 +137,35 @@
         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
--- 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 @@
         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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -145,6 +145,9 @@
 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.
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list