-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Description
Bug report
Bug description:
When an exception happens in the __exit__
part of a context manager, the
with ...
line is part of the stacktrace. This is surprising (in the bad
way), because that line is not a part of the stacktrace while you're in the
associated block of code.
In other words: at the end (either the premature end through an exception, or
when the end of the end of the block is reached) the frame for the with
statements gets pushed onto the traceback. Even though at that time nothing
from that line is being executed.
What I find particularly confusing (wrong?) is that this approach is suggestive
of the fact that code from the line of the with
statement is being executed
at the time of of failure.
OTOH, I kinda understand why the line is there: it's to explain why one arrives
at __exit__
. But that's not really through that line, it's more indirectly
through something like "exiting the with statement from line xx". (in the clean
exit case, this would arguable be at the end of the block).
Some example code and tracebacks:
knockknock
# knockknock.py -- this demonstrates the weridness of 'with' showing up
class Context:
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
raise Exception("Who's there? One line too many?")
def function_that_fails():
with Context():
pass
function_that_fails()
$ python knockknock.py
Traceback (most recent call last):
File "knockknock.py", line 16, in <module>
function_that_fails()
File "knockknock.py", line 12, in function_that_fails <= what's this doing here?
with Context(): <= what's this doing here?
File "knockknock.py", line 8, in __exit__
raise Exception("Who's there? One line too many?")
Exception: Who's there? One line too many?
Compare this with an exception inside a with-statement, where the __exit__
itself is without problems.
nobody
# nobody.py -- shows that if an exception is raised inside the context manager, the line "with" does _not_ show up
class Context:
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
pass
def function_that_fails():
with Context():
raise Exception("No frame for the Context this time")
function_that_fails()
$ python nobody.py
Traceback (most recent call last):
File "nobody.py", line 16, in <module>
function_that_fails()
File "nobody.py", line 13, in function_that_fails
raise Exception("No frame for the Context this time")
Exception: No frame for the Context this time
I'm going to call this a bug because I have no way of (concisely) explaining why
this would be the correct behavior. In general, this would be my explanation of
a stacktrace:
A stacktrace shows the sequence of function calls that led to a
specific point in a program, where each line corresponds to a function call,
including its location in the code.
This explanation breaks down for the above. Could it be amended without (at least)
doubling in length?
CPython versions tested on:
3.10, 3.12
Operating systems tested on:
Linux