Python's standard logger provides an exception() method, which is to be called from except blocks to log out exception traces by pulling from sys.exc_info().
Per https://github.com/python/cpython/blob/3.9/Lib/logging/__init__.py#L617 , logger.exception('my message') eventually boils down to something like traceback.print_exception(ei[0], ei[1], ei[2], None, some_buffer) where ei is the sys.exc_info().
However, the traceback code generates that printout by constructing a TracebackException recursively for every `except` context.
In turn, if a RecursionError was generated from an exception thrown across many except blocks, the TracebackException construction itself will have a RecursionError. This is particularly bad in cases where you'd want to capture this failure information, such as when you're printing the exception, since you'll never find out about the originating error.
Certain (well-used) libraries rely on try/except for control flow, and occasionally do recurse in their except blocks, so such exceptions are not hypothetical.
A solution to this might be to avoid constructing a TracebackException, and instead unwind traceback context using a while loop.
|
With 3.9, exc.py produces for me
Traceback (most recent call last):
File "F:\Python\a\tem3.py", line 3, in f
raise ValueError('hello')
ValueError: hello
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "F:\Python\a\tem3.py", line 3, in f
raise ValueError('hello')
ValueError: hello
...
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "F:\Python\a\tem3.py", line 11, in <module>
f()
File "F:\Python\a\tem3.py", line 5, in f
f()
File "F:\Python\a\tem3.py", line 5, in f
f()
File "F:\Python\a\tem3.py", line 5, in f
f()
[Previous line repeated 992 more times]
File "F:\Python\a\tem3.py", line 3, in f
raise ValueError('hello')
RecursionError: maximum recursion depth exceeded while calling a Python object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Programs\Python310\lib\runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Programs\Python310\lib\runpy.py", line 87, in _run_code
exec(code, run_globals)
File "F:\Python\a\tem3.py", line 14, in <module>
print_exception(exc_info[0], exc_info[1], exc_info[2], None)
File "C:\Programs\Python310\lib\traceback.py", line 113, in print_exception
for line in TracebackException(
File "C:\Programs\Python310\lib\traceback.py", line 503, in __init__
context = TracebackException(
File "C:\Programs\Python310\lib\traceback.py", line 503, in __init__
context = TracebackException(
File "C:\Programs\Python310\lib\traceback.py", line 503, in __init__
context = TracebackException(
[Previous line repeated 494 more times]
RecursionError: maximum recursion depth exceeded
This ends the same way as in #42848.
The patch in #42848 will appear in 3.10.0a5 in March. Guido and Irit decided not to backport at the patch is as much a refactoring as a fix.
On a fresh repository build, exc.py ends with
Traceback (most recent call last):
File "f:\python\a\tem3.py", line 3, in f
raise ValueError('hello')
ValueError: hello
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "f:\python\a\tem3.py", line 11, in <module>
f()
File "f:\python\a\tem3.py", line 5, in f
f()
File "f:\python\a\tem3.py", line 5, in f
f()
File "f:\python\a\tem3.py", line 5, in f
f()
[Previous line repeated 995 more times]
File "f:\python\a\tem3.py", line 3, in f
raise ValueError('hello')
RecursionError: maximum recursion depth exceeded while calling a Python object
got to the finish line!
I believe this is the expected improvement.
|