Message361783
In bpo-39386, the 'aclose' method for async generators was fixed so that the following broken code would correctly raise an error:
# -- bad code --
async def agen_fn():
yield
async def do_bad_thing():
agen = agen_fn()
aclose_coro = agen.aclose()
await aclose_coro
# Should raise RuntimeError:
await aclose_coro
asyncio.run(do_bad_thing())
# -----------------
However, the merged fix also broke the following correct code, which should *not* raise an error:
# -- good code --
async def agen_fn():
yield
async def do_good_thing():
agen = agen_fn()
await agen.aclose()
# Should *not* raise an error, but currently does in Python dev branches:
await agen.aclose()
asyncio.run(do_good_thing())
# ----------------
It's not supported to iterate the same coroutine object twice. However, making two *independent* calls to aclose should return two independent coroutine objects, and it should be legal to iterate over each object once.
This can also occur even if there's only a single call to 'aclose'. For example, this is the recommended idiom for making sure that an async generator correctly closes all its resources:
# -- good code --
async def agen_fn():
yield
async def careful_loop():
agen = agen_fn()
try:
async for _ in agen:
pass
finally:
await agen.aclose()
asyncio.run(careful_loop())
# -------------------
On released Python, the code above runs correctly. On the latest Python dev branches, it raises a RuntimeError.
So basically the problem is that the fix for bpo-39386 is storing the "was aclose iterated?" state on the async generator object, but in fact it needs to be stored on the aclose coroutine object. |
|
Date |
User |
Action |
Args |
2020-02-11 06:35:23 | njs | set | recipients:
+ njs, ned.deily, asvetlov, lukasz.langa, yselivanov |
2020-02-11 06:35:23 | njs | set | messageid: <1581402923.1.0.805434272092.issue39606@roundup.psfhosted.org> |
2020-02-11 06:35:23 | njs | link | issue39606 messages |
2020-02-11 06:35:22 | njs | create | |
|