在退出时自动关闭 aiohttp 会话 python

Automatically close an aiohttp session at exit python

我希望能够使用 atexit 自动关闭 aiohttp 会话,但我无法弄清楚为什么这不起作用。

我有以前工作的代码,但是一旦我在模块外部和文件内部定义了事件循环,其中使用的模块就会中断。

简化版代码:

按预期工作,但 public 函数不是异步的

# main.py
import module

client = module.Client()
client.foo()

# module/client.py
import asyncio
import atexit
import aiohttp

class Client:
    def __init__(self, loop=None):
        self.loop = asyncio.get_event_loop() if loop is None else loop
        atexit.register(self.close)
        self._session = aiohttp.ClientSession(loop=self.loop)

    def _run(self, future):
        return self.loop.run_until_complete(future)

    def close(self):
        self._run(self._session.close())

    def foo(self):
        ...
        self._run(...)
        ...

没有按预期工作,public 函数是异步的并且循环在 main.py

中定义
# main.py
import module
import asyncio

async def main():
    client = module.Client()
    await client.foo()

asyncio.run(main())

# module/client.py
import asyncio
import atexit
import aiohttp

class Client:
    def __init__(self, loop=None):
        self.loop = asyncio.get_event_loop() if loop is None else loop
        atexit.register(self.close)
        self._session = aiohttp.ClientSession(loop=self.loop)

    def _run(self, future):
        return self.loop.run_until_complete(future)

    def close(self):
        self._run(self._session.close())

    async def foo(self):
        ...
        await ...
        ...

第二个代码段引发错误Event loop is closed.

我基于 Whosebug 上的类似问题尝试过的解决方案给出了错误 the event loop is already runningthere is no current event loop in thread.

是否有解决方案可以在其他地方创建事件循环时自动关闭ClientSession? public 函数是异步的,如果不是等待其中的所有内容,这有关系吗?

任何帮助将不胜感激,谢谢!

我很确定 atexit 没有工作,因为您正在尝试 运行 异步代码,因此在循环关闭之前它不会被调用,您可能需要一个with 对象通过添加

async def __aexit__(self, *error_details): 
    # await but don't return, if exit returns truethy value it suppresses exceptions.
    await self._run(self._session.close())
async def __aenter__(self):
    return self

Client class 然后主代码可以使用 async with 确保客户端在主函数内关闭:

async def main():
    async with module.Client() as client:
        await client.foo()

这使得 __aexit__ 方法在 with 块完成时被调用,以确保客户端关闭,类似于使用 with 语句关闭文件等方式。

否则你想要 .close() 到 return 可等待对象,这样你就可以在主函数中调用它并等待关闭事件循环完成:

async def main():
    client = module.Client()
    await client.foo()
    await client.close() # requires .close() to return the awaitable