Skip to content

line with "with" should not be on the stacktrace for exceptions raised in __exit__ #126932

@vanschelven

Description

@vanschelven

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions