gh-135736: Fix interaction between TaskGroup and aclose() - #154649
gh-135736: Fix interaction between TaskGroup and aclose()#154649msullivan wants to merge 3 commits into
Conversation
0b648b6 to
5b945dd
Compare
Currently if you have a generator like:
```
async def test():
async with asyncio.TaskGroup() as tg:
async for x in whatever():
yield x
```
If aclose() is called on the generator and GeneratorExit is raised,
the TaskGroup's __aexit__ will raise a BaseExceptionGroup, and that
will be raised from the aclose() call instead of it being swallowed.
Fix this by raising GeneratorExit from __aexit__ when:
1. The body of task group raised it
2. No subtasks raised exceptions
This makes GeneratorExit work without swallowing other exceptions
(like it would if we just added it to _is_base_error).
5b945dd to
147f0f4
Compare
|
A slightly different approach that @1st1 and I discussed would be to add a class: and raise that, wrapping the original GeneratorExit, instead of just raising the original GeneratorExit. This has the advantage that the behavior changes less: a BaseExceptionGroup still gets raised, it's just also now a GeneratorExit. I have an implementation of this and it works fine, but the idea of having to documen this exception for end users seemed unfortunate to me, so I didn't do it for this PR. Any thoughts on this? |
Documentation build overview
19 files changed ·
|
|
This PR seems to do the same as #154538 |
Huh, very funny that the bug sits around for a year and attracts two competing PRs in the same week. Anyway, I think the approaches and behaviors of the two PRs are quite different, though. I think this approach is preferable, because it doesn't drop exceptions on a GeneratorExit, which is not a particularly exceptional case. (Unlike SystemExit and KeyboardInterrupt) |
Yeah, that's quite important. @kumaraditya303 Yeah, this PR has a better approach. Basically,
IMO this PR is fine. We should salvage the logging part of https://github.com/python/cpython/pull/154538/changes in a separate PR though. |
|
@msullivan I've approved the PR but I'll let it sit for some time before hitting the button in case Kumar or others have more comments. |
|
|
||
| .. versionchanged:: 3.16 | ||
|
|
||
| Addition of the special case for :exc:`GeneratorExit`. |
There was a problem hiding this comment.
I think we are backporting this, in that case it doesn't make sense to keep a version changed entry for 3.16
There was a problem hiding this comment.
Should I mark it for 3.15, then?
Or are we backporting further than that
There was a problem hiding this comment.
I've updated it to say 3.15. Let me know if you want something else
There was a problem hiding this comment.
I think you should remove it entirely because it will be backported till 3.13.
There was a problem hiding this comment.
Would we just totally drop the new documentation of this, then?
It makes me nervous to have it documented there without any versionchanged notice?
We could also list the other point releases it is in; there's precedent for that in the docs (and in asyncio docs specifically)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Currently if you have a generator like:
If aclose() is called on the generator and GeneratorExit is raised,
the TaskGroup's
__aexit__will raise a BaseExceptionGroup, and thatwill be raised from the aclose() call instead of it being swallowed.
Fix this by raising GeneratorExit from aexit when:
This makes GeneratorExit work without swallowing other exceptions
(like it would if we just added it to _is_base_error).