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