-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
aclose() doesn't stop raise StopAsyncIteration / GeneratorExit to __anext__() #78911
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
Comments
I expected an async-generator's aclose() method to cancel it's __anext__(). Instead, the task isn't cancelled, and (it seems) manually cleanup is necessary. @pytest.mark.asyncio async def make_agen():
try:
await event.wait()
raise NotImplementedError()
yield # pylint: disable=W0101, unreachable
finally:
nonlocal done
done = True
async def run():
nonlocal agen
agen = make_agen()
await agen.__anext__()
task = asyncio.ensure_future(run())
await asyncio.sleep(.01)
Looking at the tests for CPython, the implementation seems to expect it, but I'm unclear if PEP-525 actually intends for this to be the case: cpython/Lib/test/test_asyncgen.py Line 657 in e42b705
Alternatively, maybe I shouldn't be calling aclose, and instead I should be cancelling the task: @pytest.mark.asyncio async def make_agen():
try:
await event.wait()
raise NotImplementedError()
yield # pylint: disable=W0101, unreachable
finally:
nonlocal done
done = True
async def run():
nonlocal agen
agen = make_agen()
await agen.__anext__()
task = asyncio.ensure_future(run())
await asyncio.sleep(.01)
task.cancel()
await asyncio.sleep(.01)
So the "bug" here is one of PEP-525 clarification. |
I've worked around this problem by doing something like this: async def close_cancelling(agen):
while True:
try:
task = asyncio.ensure_future(agen.__anext__())
await task
yield task.result()
except (GeneratorExit, StopAsyncIteration):
await agen.aclose()
task.cancel()
break
async def run():
try:
async for v in close_cancelling(agen):
received.append(v)
except asyncio.CancelledError:
# handle finalization
pass Though, I'm still convinced that |
Interesting. Nathaniel, do you have this problem in Trio (aclose() not cancelling an anext()) or cancel scopes solve this problem somehow? |
Part of the issue here is the one discussed in bpo-30773 / bpo-32526: async generators allow themselves to be re-entered while another asend/athrow/aclose call is in progress, and it causes weird and confusing results. So at a minimum, trying to call aclose while another task is calling asend should make aclose raise an error saying that the generator is already busy. Of course the OP wants to go further and have aclose actually trigger a cancellation of any outstanding asend/athrow. I see the intuition here, but unfortunately I don't think this can work. Doing a cancellation requires some intimate knowledge of the coroutine runner. You can't just throw in an exception; you also have to unwind the task state. (Eg in the example with 'event.wait', you need to somehow tell the event object that this task is no longer waiting for it, so it shouldn't be notified when the event occurrs.) So I think we just need to fix ag_running and then recommend people find other ways to interrupt running async generators. |
Agree. Speaking of fixing ag_running, could you please review my PR: #7468 I can then commit it and maybe get this fixed in 3.7.1. |
#7468 (prohibit asend/etc. reentrance) was merged ~a year ago. Perhaps it's time to restart the original discussion, whether |
I'm still not aware of any way to make this work, technically. So it's a moot point unless someone has a proposal. |
Then perhaps the issue should be closed 🤔 |
IMO we should not change anything here but recommend the users to not close async generators concurrently with docs. Doing any change here is going to be both complex probably backwards incompatible as users might be relying on the current behavior. |
Sounds good. I agree that the behavior by now is ingrained and will be hard to change without breaking working user code, and not really worth it. So let's document it. |
Documentation will be taken care of in #100108 |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: