This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: traceback.FrameSummary does not handle exceptions from `repr()`
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: andrei.avk, blueyed, iritkatriel, moi90, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2020-01-06 04:14 by blueyed, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 17855 closed blueyed, 2020-01-06 04:23
Messages (11)
msg359400 - (view) Author: daniel hahler (blueyed) * Date: 2020-01-06 04:14
Exceptions within `__repr__` methods of captured locals
(e.g. via the `capture_locals` argument of `TracebackException`) are not handled:

```
import traceback


class CrashingRepr:
    def __repr__(self):
        raise RuntimeError("crash")


traceback.FrameSummary("fname", 1, "name", locals={"crash": CrashingRepr()})
```

Result:
```
Traceback (most recent call last):
  File "test_framesummary_repr.py", line 9, in <module>
    traceback.FrameSummary("fname", 1, "name", locals={"crash": CrashingRepr()})
  File "…/pyenv/3.8.0/lib/python3.8/traceback.py", line 260, in __init__
    self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
  File "…/pyenv/3.8.0/lib/python3.8/traceback.py", line 260, in <dictcomp>
    self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
  File "test_framesummary_repr.py", line 6, in __repr__
    raise RuntimeError("crash")
RuntimeError: crash
```

The following patch would fix this:

```diff
diff --git i/Lib/traceback.py w/Lib/traceback.py
index 7a4c8e19f9..eed7082db4 100644
--- i/Lib/traceback.py
+++ w/Lib/traceback.py
 class FrameSummary:
     """A single frame from a traceback.

@@ -257,7 +265,17 @@ def __init__(self, filename, lineno, name, *, lookup_line=True,
         self._line = line
         if lookup_line:
             self.line
-        self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
+        if locals:
+            self.locals = {}
+            for k, v in locals.items():
+                try:
+                    self.locals[k] = repr(v)
+                except (KeyboardInterrupt, SystemExit):
+                    raise
+                except BaseException as exc:
+                    self.locals[k] = f"<unrepresentable repr ({exc})>"
+        else:
+            self.locals = None

     def __eq__(self, other):
         if isinstance(other, FrameSummary):

```
msg359770 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2020-01-11 00:32
As is true for most special methods, it is a bug for __repr__ methods to raise.  They should return a string, as documented.

Special method wrappers generally assume that the wrapped methods work.  In particular, repr assumes this, and so do the __repr__ methods of all collections classes.  They do not try to hide bugs.  Example:

>>> class BadRep:
	def __repr__(self): 1/0

	
>>> br = BadRep()
>>> [br]
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    [br]
  File "C:\Programs\Python39\lib\idlelib\rpc.py", line 620, in displayhook
    text = repr(value)
  File "<pyshell#6>", line 2, in __repr__
    def __repr__(self): 1/0
ZeroDivisionError: division by zero

Bugs should be reported, not masked.  I don't think that FrameSummary should be an exception to this.  Therefore I think that this issue should be closed (along with the PR) as 'not a bug'.
msg389844 - (view) Author: Martin (moi90) * Date: 2021-03-30 19:08
> As is true for most special methods, it is a bug for __repr__ methods to raise.

Is this codified anywhere? I only learned about that in this bug report.
msg389852 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-03-30 21:10
__repr__() and repr() can raise exceptions. Silencing arbitrary exception is usually a bad idea. Even correctly raised code can raise exceptions such as MemoryError, RecursionError and KeybordInterrupt.

For this reason I think that the proposed change should not be made.
msg389859 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-03-30 21:38
If we decide to reject this then we should add unit tests that show this state of affairs is deliberate.
msg389861 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-03-30 21:46
See also issue43656.
msg389889 - (view) Author: Martin (moi90) * Date: 2021-03-31 11:32
> Even correctly raised code can raise exceptions such as MemoryError, RecursionError and KeybordInterrupt.

For me, this is an argument in favor of the change (although KeybordInterrupt and probably a couple more should not be caught).

traceback is used in contexts where an error already occured and it should do its best to help the user find the cause for it. It does not help much if itself then fails because some values are not repr'eable.

What does pdb do in this case?
msg389894 - (view) Author: Martin (moi90) * Date: 2021-03-31 12:37
pdb uses vanilla repr as well:

https://github.com/python/cpython/blob/f3ab670fea75ebe177e3412a5ebe39263cd428e3/Lib/pdb.py#L1180
msg393200 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-05-07 18:27
I think pdb is a different story because it's an interactive application rather than a library. pdb can decide that it prints an error to the screen and returns to the interactive prompt, if that is appropriate. The functions of the traceback module don't have application context telling them what they should do with an exception.
msg393297 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-05-09 05:28
This probably shouldn't be done, for all of the reasons above.

For the sake of an argument, if we really wanted to do something like this (as it may be convenient in some cases), a better way might be to add a `__tb_repr__ = __repr__` which can be overridden, and `traceback` would use that instead of plain repr.
msg405933 - (view) Author: Martin (moi90) * Date: 2021-11-08 07:23
I submitted a pull request for the related issue43656:

https://github.com/python/cpython/pull/29299

It introduces a format_locals parameter that enables the customized formatting of a FrameSummary.

This way, a user of traceback could provide a function that handles exceptions within __repr__ if necessary.
History
Date User Action Args
2022-04-11 14:59:25adminsetgithub: 83409
2021-11-08 07:23:59moi90setmessages: + msg405933
2021-05-12 14:54:11iritkatrielsetstatus: open -> closed
resolution: not a bug
stage: patch review -> resolved
2021-05-09 08:10:08terry.reedysetnosy: - terry.reedy

versions: + Python 3.11, - Python 3.7, Python 3.8, Python 3.9
2021-05-09 05:28:58andrei.avksetnosy: + andrei.avk
messages: + msg393297
2021-05-07 18:27:48iritkatrielsetmessages: + msg393200
2021-03-31 12:37:06moi90setmessages: + msg389894
2021-03-31 11:32:19moi90setmessages: + msg389889
2021-03-30 21:46:40serhiy.storchakasetmessages: + msg389861
2021-03-30 21:38:26iritkatrielsetnosy: + iritkatriel
messages: + msg389859
2021-03-30 21:10:19serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg389852
2021-03-30 19:08:51moi90setnosy: + moi90
messages: + msg389844
2020-01-11 00:32:09terry.reedysetnosy: + terry.reedy

messages: + msg359770
versions: - Python 3.5, Python 3.6
2020-01-06 04:23:10blueyedsetkeywords: + patch
stage: patch review
pull_requests: + pull_request17276
2020-01-06 04:14:25blueyedcreate