-
-
Notifications
You must be signed in to change notification settings - Fork 367
Suppress GeneratorExit from .aclose()
for as_safe_channel
#3325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e334c94
Suppress `.aclose()`'s GeneratorExit in exception groups
A5rocks 14bb557
Add a newsfragment
A5rocks 1904b98
Fix type issue
A5rocks 7993fac
Address coverage
A5rocks b1fda4f
Try one more coverage workaround
A5rocks d98b85e
PR review
A5rocks ea761df
Appease mypy + PR review
A5rocks 1af5620
Address coverage
A5rocks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Avoid having `trio.as_safe_channel` raise if closing the generator wrapped | ||
`GeneratorExit` in a `BaseExceptionGroup`. |
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
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible that we split out multiple
GeneratorExit
here and suppress one too many?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if someone raises GeneratorExit in a child task + aclose raises in a nursery. I'm not sure if we should care though, since for instance .close doesn't care if you raise the GeneratorExit or if it does. (I forgot to check if Python suppresses GeneratorExit from next(gen) too... I'll check in a bit)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
except* GeneratorExit: pass
do what we want here?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would have the same behavior as the .split done here
(well except if there's 1 other exception? Not too sure.), but I think the issue @jakkdl is talking about is that this implementation can suppress a user's exception.I'm not sure it's worth fixing this case though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just don't see a way to avoid suppressing a user-raised
GeneratorExit
, but also that seems incredibly unlikely to arise in practice so 🤷and if it's equivalent I tend to prefer "less code" pretty strongly
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be possible to reimplement
.aclose()
and remove the raisedGeneratorExit
by identity, but I note that without an exception group Python doesn't care about this. (I agree that I would prefer what is currently here over implementing that)I guess another possibility is to keep every exception but the first GeneratorExit, though that might toss the user-raised one anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
python not distinguishing the source of
GeneratorExit
is a pretty good argument for us not caring either.We could raise a warning or something
if len(_.exceptions) > 1
if we don't wanna bother adding weird logic, it could lead to weird bugs for users, and it's sufficiently rare. Or even do anassert
with a message telling them to open an issue if they care about the behavior.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except* GeneratorExit: pass
is IMO actually better behavior than the.split()
-based approach: the latter wraps whatever we had into an exception group, whileexcept* GeneratorExit: pass
leaves bare exceptions untouched.This is a big deal for us, because the more layers of
ExceptionGroup
in your tracebacks the harder they get to read, and this utility function is going to be used all the time - dropping a layer from tracebacks was a huge attraction of@trio.as_safe_generator
relative to my older helper function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aside from that, I'd be keen to merge and ship 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that too! But it turns out that my understanding of exception groups is not great:
So I think
.split
is literally identical toexcept* GeneratorExit: pass
.This feels weird but weighing the amount of time someone might spend figuring out this as a bug vs being bothered by it, I think it makes sense.