gh-102201: Preserve exception __context__ chain in ExitStack/AsyncExitStack#152151
Open
iamsharduld wants to merge 1 commit into
Open
gh-102201: Preserve exception __context__ chain in ExitStack/AsyncExitStack#152151iamsharduld wants to merge 1 commit into
iamsharduld wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ExitStack/AsyncExitStacktry to reproduce the exception__context__chaining of equivalent nestedwithstatements. That works while an exception is being handled, but when__exit__runs after the body completed normally (no active exception) and several callbacks raise during unwind, only the last exception propagates — the earlier ones are dropped from the__context__chain (and from the traceback).As described on the issue, the interpreter only chains an exception raised by a callback onto the exception that is currently being handled. When the body exited normally there isn't one, so ExitStack has nothing to chain onto, and after the fact it can't tell "
__context__isNonebecause there was nothing to chain" apart from "__context__isNonebecause the callback cleared it".This makes the missing exception active again: when
__exit__runs with no exception being handled and a previous callback has already raised, the most recent exception is briefly re-raised (saving and restoring its__traceback__) so the interpreter chains the next callback's exception onto it, exactly as nestedwithstatements do._fix_exception_contextis unchanged and still handles the case where an exception is being handled.I made the real previous exception active rather than introducing a private sentinel (the other option floated on the issue), because a sentinel is observable from callbacks via
sys.exception(), a bareraise, orraise ... from sys.exception(), which would leak an internal exception object into user code. With this approach those all reference the real previous exception (or raiseRuntimeError: No active exception to reraisewhen nothing is active), matching nestedwith.Tests cover the chaining, the bare-
raiseandraise ... from sys.exception()cases, and the existing explicit__context__ = None/ issue 20317 behaviour is preserved, for bothExitStackandAsyncExitStack.Fixes #102201.