According to this blog, normal generators will be immediately closed when there is no reference to it. (CPython exclusive though). My question is "Does this apply to async-generators that don't have 'await' in its finally-block?"
Motivation
I'm an auther of an async library, and want async-generators to work like the normal ones. Otherwise, the user have to code something like this
agen = async_generator_function()
async with async_closing(agen):
async for v in agen:
if some_condition:
break
do_something()
instead of this
async for v in async_generator_function():
if some_condition:
break
do_something()
Which is pretty annoying. Luckly its unit test always works as I expected so I want to know if it's guaranteed or not.
Investigation
The following code
import asyncio
async def agen_func():
try:
for i in range(10):
yield i
finally:
print('finalized')
async def main():
async for i in agen_func():
print(i)
if i > 2:
break
print('end of main()')
asyncio.run(main())
printed
0
1
2
3
end of main()
finalized
And the following code
import trio
async def agen_func():
try:
for i in range(10):
yield i
finally:
print('finalized')
async def main():
async for i in agen_func():
print(i)
if i > 2:
break
print('end of main()')
trio.run(main)
printed the same, which looks like both asyncio
and trio
say "NO" to my question, because if async-generators were closed immediately as I expected, they would have printed finalized
before end of main()
. Actually, if you use those two async libraries, the answer to my question probably is "NO".
But here is an interesting thing. If you don't use them, async-generators work as I expected.
async def agen_func():
try:
for i in range(10):
yield i
finally:
print('finalized')
async def main():
async for i in agen_func():
print(i)
if i > 2:
break
print('end of main()')
try:
main().send(None)
except StopIteration:
pass
0
1
2
3
finalized
end of main()
And this is how the async library I'm currently developing works.
set_asyncgen_hooks
According to PEP525, asyncio
does some hacky stuff called sys.set_asyncgen_hooks()
. trio
does it too. I believe this is the reason why async-generators don't work as I expected under them. And it's understandable because it's necessary for the async-generators whose finally-block contains await
.
more precise question
So my question is:
If you are using CPython, and the async-library you are using doesn't do the hacky stuff above, then async-generators that don't have await
in its finally-block will be immediately closed when there is no reference to it?
environment
- CPython 3.8.1
- Trio 0.17.0