当没有对它的引用时,它的 finally 块中没有“await”的异步生成器会立即关闭吗?

Nat*_*itō 8 asynchronous cpython python-3.x

根据这个博客,正常的生成器在没有引用时会立即关闭。(不过是 CPython 独有的)。我的问题是“这是否适用于在 finally 块中没有‘await’的异步生成器?”

动机

我是一个异步库的作者,并希望异步生成器像普通的一样工作。否则,用户必须编写这样的代码

    agen = async_generator_function()
    async with async_closing(agen):
        async for v in agen:
            if some_condition:
                break
            do_something()
Run Code Online (Sandbox Code Playgroud)

而不是这个

    async for v in async_generator_function():
        if some_condition:
            break
        do_something()
Run Code Online (Sandbox Code Playgroud)

这很烦人。幸运的是,它的单元测试总是按我的预期工作,所以我想知道它是否有保证。

调查

以下代码

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())
Run Code Online (Sandbox Code Playgroud)

打印

0
1
2
3
end of main()
finalized
Run Code Online (Sandbox Code Playgroud)

以及以下代码

0
1
2
3
end of main()
finalized
Run Code Online (Sandbox Code Playgroud)

打印了相同的内容,看起来两者都一样,asyncio并且trio对我的问题说“不”,因为如果异步生成器按照我的预期立即关闭,它们会finalizedend of main(). 实际上,如果您使用这两个异步库,我的问题的答案可能是“否”。

但这里有一件有趣的事情。如果您不使用它们,异步生成器会按我的预期工作。

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
Run Code Online (Sandbox Code Playgroud)
0
1
2
3
finalized
end of main()
Run Code Online (Sandbox Code Playgroud)

这就是我目前正在开发的异步库的工作方式。

set_asyncgen_hooks

根据PEP525asyncio做了一些名为sys.set_asyncgen_hooks(). trio也这样做。我相信这就是异步生成器在它们下不能像我预期的那样工作的原因。这是可以理解的,因为它对于 finally 块包含await.

更准确的问题

所以我的问题是:

如果您使用的是 CPython,并且您使用的异步库没有执行上述操作,那么await在没有引用它的情况下,finally 块中没有的异步生成器将立即关闭?

环境

  • CPython 3.8.1
  • 三重奏 0.17.0