Skip to content

Commit 46fc584

Browse files
authored
pythongh-87822: Make traceback module robust to exceptions from repr() of local values (pythonGH-94691)
1 parent da71751 commit 46fc584

File tree

5 files changed

+14
-3
lines changed

5 files changed

+14
-3
lines changed

Doc/library/traceback.rst

+4
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ capture data for later printing in a lightweight fashion.
341341
local variables in each :class:`FrameSummary` are captured as object
342342
representations.
343343

344+
.. versionchanged:: 3.12
345+
Exceptions raised from :func:`repr` on a local variable (when
346+
*capture_locals* is ``True``) are no longer propagated to the caller.
347+
344348
.. classmethod:: from_list(a_list)
345349

346350
Construct a :class:`StackSummary` object from a supplied list of

Lib/test/test_traceback.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,9 @@ def format_frame_summary(self, frame_summary):
22792279
f' File "{__file__}", line {lno}, in f\n 1/0\n'
22802280
)
22812281

2282+
class Unrepresentable:
2283+
def __repr__(self) -> str:
2284+
raise Exception("Unrepresentable")
22822285

22832286
class TestTracebackException(unittest.TestCase):
22842287

@@ -2546,12 +2549,13 @@ def test_locals(self):
25462549
linecache.updatecache('/foo.py', globals())
25472550
e = Exception("uh oh")
25482551
c = test_code('/foo.py', 'method')
2549-
f = test_frame(c, globals(), {'something': 1, 'other': 'string'})
2552+
f = test_frame(c, globals(), {'something': 1, 'other': 'string', 'unrepresentable': Unrepresentable()})
25502553
tb = test_tb(f, 6, None, 0)
25512554
exc = traceback.TracebackException(
25522555
Exception, e, tb, capture_locals=True)
25532556
self.assertEqual(
2554-
exc.stack[0].locals, {'something': '1', 'other': "'string'"})
2557+
exc.stack[0].locals,
2558+
{'something': '1', 'other': "'string'", 'unrepresentable': '<local repr() failed>'})
25552559

25562560
def test_no_locals(self):
25572561
linecache.updatecache('/foo.py', globals())

Lib/traceback.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ def __init__(self, filename, lineno, name, *, lookup_line=True,
279279
self._line = line
280280
if lookup_line:
281281
self.line
282-
self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
282+
self.locals = {k: _safe_string(v, 'local', func=repr)
283+
for k, v in locals.items()} if locals else None
283284
self.end_lineno = end_lineno
284285
self.colno = colno
285286
self.end_colno = end_colno

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,7 @@ Ed Schouten
15901590
Scott Schram
15911591
Robin Schreiber
15921592
Chad J. Schroeder
1593+
Simon-Martin Schroeder
15931594
Christian Schubert
15941595
Sam Schulenburg
15951596
Andreas Schwab
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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).

0 commit comments

Comments
 (0)