Joe Wells <bugs-python-...@jbw.link> added the comment:

I would like to request that this bug be repopened and fixed.

I've changed (or at least tried to change, I'm not sure if it will let me) the 
title of the bug to point out that the failure happens in 
FrameSummary.__init__.  It does not happen in StackSummary.format.

This problem makes the capture_locals=True feature of TracebackException and 
StackSummary and the "locals" parameter of FrameSummary unreliable.  If any one 
of the local variables in any frame on the stack is in an inconsistent state 
such that repr will raise an exception, the processing of the traceback fails.  
This kind of inconsistent state is of course likely to happen during debugging, 
which is precisely when you would want the capture_locals feature to actually 
work so you can see what is going wrong.

Just one example of an exception traceback being created with an unsafe local 
variable value in one of the stack frames is in the following line:

  from _pydecimal import Decimal as D; D(None)

The _pydecimal.Decimal.__new__ method raises an exception when it sees a value 
it doesn't know how to convert to Decimal.  When it does this, the new object 
it was creating is left in an inconsistent state missing the _sign attribute.  
When you try to inspect the resulting exception traceback with 
traceback.TracebackException(..., capture_locals=True), this raises an 
exception.

While I was tracking this bug down, I discovered that the 
traceback.TracebackException.__init__ method has the same problem: it 
initializes the _str attribute used by the __str__ method quite late and when 
it raised an exception before this point, the incompletely initialized 
TracebackException object caused repr to fail.  There's at least one more class 
in traceback.py that also has this problem, but I don't remember which one it 
is.

The cascade of exceptions causing exceptions causing exceptions makes the 
capture_locals feature difficult to use and debug.

Here is a short snippet that reproduces the bug on Python 3.9.7:

import traceback
class TriggerTracebackBug:
    def __init__(self):
        raise RuntimeError("can't build a TriggerTracebackBug object for some 
reason")
        self._repr = 'if we reached this line, this object would have a repr 
result'
    def __repr__(self):
        return self._repr
try:
    TriggerTracebackBug()
except Exception as e:
    # this method call fails because there is a stack frame with a local
    # variable (self) such that repr fails on that variable's value:
    traceback.TracebackException.from_exception(e, capture_locals=True)

It's clear that it is too much to expect every class to implement a safe 
__repr__ method.  So it also seems clear to me that 
traceback.FrameSummary.__init__ is the place to fix it.

My suggested fix is to replace this line in the latest Lib/traceback.py:

        self.locals = {k: repr(v) for k, v in locals.items()} if locals else 
None

with something like this code (written in a somewhat awkward way because that 
helped while I was debugging it):

        if locals:
            d = {}
            self.locals = d
            for k, v in locals.items():
                try:
                    d[k] = repr(v)
                except Exception:
                    d[k] = '''<an exception was raised while trying to format 
this local variable's value>'''
        else:
            self.locals = None

I've tested this code in an older version of Python and it fixed the problem 
for me.

----------
nosy: +jbw
title: StackSummary.format fails if repr(value) fails -> TracebackException or 
StackSummary.extract with capture_locals=True fail to catch exceptions raised 
by repr() on value of frame local variable in FrameSummary.__init__.

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue43656>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to