微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

外部异步上下文管理器在内部异步生成器之前完成

如何解决外部异步上下文管理器在内部异步生成器之前完成

给出以下最小示例:

@asynccontextmanager
async def async_context():
    try:
        yield
    finally:
        await asyncio.sleep(1)
        print('finalize context')


async def async_gen():
    try:
        yield
    finally:
        await asyncio.sleep(2)
        # will never be called if timeout is larger than in async_context
        print('finalize gen')


async def main():
    async with async_context():
        async for _ in async_gen():
            break


if __name__ == "__main__":
    asyncio.run(main())

我正在 break 迭代异步生成器,我希望 finally 块在我的异步上下文管理器 finally 块运行之前完成。在这个例子中,"finalize gen" 永远不会被打印,因为程序在这之前就退出了。

请注意,我特意在生成2 块中选择了 finally 超时,以便上下文管理器 finally 有机会在此之前运行。如果我为两个超时选择 1,则将打印两条消息。

这是一种竞争条件吗?我希望在程序完成之前完成所有 finally 块。

如何防止上下文管理器 finally 块在生成finally 块完成之前运行?

上下文:

我使用 playwright 来控制 Chrome 浏览器。外部上下文管理器提供一个页面,它在 finally 块中关闭

我正在使用 python 3.9.0

试试这个例子:https://repl.it/@trixn86/AsyncGeneratorRaceCondition

解决方法

异步上下文管理器对异步生成器一无所知。实际上,main 中的任何内容都不知道您 break 之后的异步生成器。你没有办法等待生成器的最终确定。

如果要等待生成器关闭,则需要显式处理关闭:

async def main():
    async with async_context():
        gen = async_gen()
        try:
            async for _ in gen:
                break
        finally:
            await gen.aclose()

在 Python 3.10 中,您将能够使用 contextlib.aclosing 代替 try/finally:

async def main():
    async with async_context():
        gen = async_gen()
        async with contextlib.aclosing(gen):
            async for _ in gen:
                break

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。