From 46fc584b00f2ccee5b77d8fc525881def8670a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon-Martin=20Schr=C3=B6der?= Date: Mon, 11 Jul 2022 11:14:15 +0200 Subject: gh-87822: Make traceback module robust to exceptions from repr() of local values (GH-94691) --- Doc/library/traceback.rst | 4 ++++ Lib/test/test_traceback.py | 8 ++++++-- Lib/traceback.py | 3 ++- Misc/ACKS | 1 + .../next/Library/2022-07-08-17-49-12.gh-issue-87822.F9dzkf.rst | 1 + 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-08-17-49-12.gh-issue-87822.F9dzkf.rst diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index a8412cc..8cb6af9 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -341,6 +341,10 @@ capture data for later printing in a lightweight fashion. local variables in each :class:`FrameSummary` are captured as object representations. + .. versionchanged:: 3.12 + Exceptions raised from :func:`repr` on a local variable (when + *capture_locals* is ``True``) are no longer propagated to the caller. + .. classmethod:: from_list(a_list) Construct a :class:`StackSummary` object from a supplied list of diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index f4161fb..602bd2c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2279,6 +2279,9 @@ class TestStack(unittest.TestCase): f' File "{__file__}", line {lno}, in f\n 1/0\n' ) +class Unrepresentable: + def __repr__(self) -> str: + raise Exception("Unrepresentable") class TestTracebackException(unittest.TestCase): @@ -2546,12 +2549,13 @@ class TestTracebackException(unittest.TestCase): linecache.updatecache('/foo.py', globals()) e = Exception("uh oh") c = test_code('/foo.py', 'method') - f = test_frame(c, globals(), {'something': 1, 'other': 'string'}) + f = test_frame(c, globals(), {'something': 1, 'other': 'string', 'unrepresentable': Unrepresentable()}) tb = test_tb(f, 6, None, 0) exc = traceback.TracebackException( Exception, e, tb, capture_locals=True) self.assertEqual( - exc.stack[0].locals, {'something': '1', 'other': "'string'"}) + exc.stack[0].locals, + {'something': '1', 'other': "'string'", 'unrepresentable': ''}) def test_no_locals(self): linecache.updatecache('/foo.py', globals()) diff --git a/Lib/traceback.py b/Lib/traceback.py index 55f8080..b1a5fd0 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -279,7 +279,8 @@ class FrameSummary: self._line = line if lookup_line: self.line - self.locals = {k: repr(v) for k, v in locals.items()} if locals else None + self.locals = {k: _safe_string(v, 'local', func=repr) + for k, v in locals.items()} if locals else None self.end_lineno = end_lineno self.colno = colno self.end_colno = end_colno diff --git a/Misc/ACKS b/Misc/ACKS index b634041..32475f8 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1590,6 +1590,7 @@ Ed Schouten Scott Schram Robin Schreiber Chad J. Schroeder +Simon-Martin Schroeder Christian Schubert Sam Schulenburg Andreas Schwab diff --git a/Misc/NEWS.d/next/Library/2022-07-08-17-49-12.gh-issue-87822.F9dzkf.rst b/Misc/NEWS.d/next/Library/2022-07-08-17-49-12.gh-issue-87822.F9dzkf.rst new file mode 100644 index 0000000..7b27f5d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-08-17-49-12.gh-issue-87822.F9dzkf.rst @@ -0,0 +1 @@ +When called with ``capture_locals=True``, the :mod:`traceback` module functions swallow exceptions raised from calls to ``repr()`` on local variables of frames. This is in order to prioritize the original exception over rendering errors. An indication of the failure is printed in place of the missing value. (Patch by Simon-Martin Schroeder). -- cgit v0.12