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
bpo-39606: allow closing async generators that are already closed #18475
Conversation
The fix for bpo-39386 attempted to make it so you couldn't reuse a agen.aclose() coroutine object. It accidentally also prevented you from calling aclose() at all on an async generator that was already closed or exhausted. This commit fixes it so we're only blocking the actually illegal cases, while allowing the legal cases. The new tests failed before this patch. Also confirmed that this fixes the test failures we were seeing in Trio with Python dev builds: python-trio/trio#1396
I think this needs to get in before 3.8.2 is released, which is currently scheduled for Monday Feb 17. |
PyErr_SetNone(PyExc_StopIteration); | ||
return NULL; | ||
} | ||
|
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.
These two hunks are the main change. Originally, this code raised StopIteration
if the generator had no frame. The fix for bpo-39386 added the check for the athrow coroutine being in AWAITABLE_STATE_CLOSED
to the same if
statement, and switched it to raise RuntimeError
in both cases. But in fact, the generator having no frame should raise StopIteration
; it's only the AWAITABLE_STATE_CLOSED
case that should raise RuntimeError
. So I split the AWAITABLE_STATE_CLOSED
logic out into its own block, and restored the old if (f == NULL || f->f_stacktop)
logic.
Everything else is just going through to make sure that we're setting AWAITABLE_STATE_CLOSED
at the right times.
if (o->agt_state == AWAITABLE_STATE_INIT) { | ||
if (o->agt_gen->ag_running_async) { | ||
o->agt_state = AWAITABLE_STATE_CLOSED; |
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.
If the athrow
/aclose
is being aborted because there's already another task advancing the underlying async generator, then that's a final result, so we should mark the coroutine as complete.
@@ -1878,7 +1884,6 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) | |||
/* aclose() mode */ | |||
if (retval) { | |||
if (_PyAsyncGenWrappedValue_CheckExact(retval)) { | |||
o->agt_gen->ag_running_async = 0; | |||
Py_DECREF(retval); | |||
goto yield_close; |
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.
yield_close
already sets ag_running_async = 0
, so the deleted line was already a no-op.
@@ -1893,16 +1898,17 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) | |||
|
|||
yield_close: | |||
o->agt_gen->ag_running_async = 0; | |||
o->agt_state = AWAITABLE_STATE_CLOSED; |
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.
ag_running_async
tracks whether there's some call to asend
/athrow
in progress. agt_state
tracks whether this call to asend
/athrow
is in progress. So every time we set ag_running_async = 0
, we should also set agt_state = AWAITABLE_STATE_CLOSED
.
Codecov Report
@@ Coverage Diff @@
## master #18475 +/- ##
=========================================
Coverage 82.11% 82.12%
=========================================
Files 1956 1955 -1
Lines 589067 583746 -5321
Branches 44436 44437 +1
=========================================
- Hits 483723 479380 -4343
+ Misses 95690 94721 -969
+ Partials 9654 9645 -9
Continue to review full report at Codecov.
|
Yeah, I thought about that when writing them, but I couldn't figure out how to write them in a way that would make sense when looking at a single version of the file, instead of a diff. The full state machine management here is complicated and spread about over a bunch of different places, and there are lots of bits of code that are the same as the ones I modified here and the comments would apply the same way, just I didn't touch them because they were already correct... Since there's some urgency here and everyone agrees the code looks good, I'm going to go ahead and merge. If you have suggestions on how to make the comments work better we can always add those later :-) |
Thanks @njsmith for the PR |
I'm having trouble backporting to |
Sorry @njsmith, I had trouble checking out the |
Thanks @njsmith for the PR |
Thanks @njsmith for the PR |
GH-18501 is a backport of this pull request to the 3.8 branch. |
Sorry, @njsmith, I could not cleanly backport this to |
GH-18502 is a backport of this pull request to the 3.7 branch. |
…ed (GH-18475) (GH-18501) The fix for [bpo-39386](https://bugs.python.org/issue39386) attempted to make it so you couldn't reuse a agen.aclose() coroutine object. It accidentally also prevented you from calling aclose() at all on an async generator that was already closed or exhausted. This commit fixes it so we're only blocking the actually illegal cases, while allowing the legal cases. The new tests failed before this patch. Also confirmed that this fixes the test failures we were seeing in Trio with Python dev builds: python-trio/trio#1396 https://bugs.python.org/issue39606 (cherry picked from commit 925dc7f) Co-authored-by: Nathaniel J. Smith <njs@pobox.com> https://bugs.python.org/issue39606 Automerge-Triggered-By: @njsmith
…-18475) (GH-18502) The fix for [bpo-39386](https://bugs.python.org/issue39386) attempted to make it so you couldn't reuse a agen.aclose() coroutine object. It accidentally also prevented you from calling aclose() at all on an async generator that was already closed or exhausted. This commit fixes it so we're only blocking the actually illegal cases, while allowing the legal cases. The new tests failed before this patch. Also confirmed that this fixes the test failures we were seeing in Trio with Python dev builds: python-trio/trio#1396 https://bugs.python.org/issue39606 (cherry picked from commit 925dc7f)
The fix for bpo-39386 attempted to make it so you couldn't reuse a
agen.aclose() coroutine object. It accidentally also prevented you
from calling aclose() at all on an async generator that was already
closed or exhausted. This commit fixes it so we're only blocking the
actually illegal cases, while allowing the legal cases.
The new tests failed before this patch. Also confirmed that this fixes
the test failures we were seeing in Trio with Python dev builds:
python-trio/trio#1396
https://bugs.python.org/issue39606
Automerge-Triggered-By: @njsmith