显式调用close()仍触发aiohttp未关闭会话警告,上下文管理器则正常
异步API封装显式调用close()出现未关闭会话警告的问题及解决
我参考异步API封装的示例,构建了一个基于aiohttp的异步API封装类AsyncWrapper,代码如下:
import asyncio import aiohttp class AsyncWrapper: def __init__(self, api_key, useragent="API-Wrapper/0.2"): self.url = f"https://example.api/v1" self._api_key = api_key self._useragent = useragent self._headers = {"X-API-Key": self._api_key, "accept": "application/json", "User-Agent": self._useragent} self._session = aiohttp.ClientSession() async def __aenter__(self): return self async def __aexit__(self, exc_type, exc_val, exc_tb): await self.close() async def close(self): await self._session.close() async def get_endpoint1(self, arg1, arg2): endpoint = "/pair" async with self._session.get(self.url + endpoint, params={"arg1": arg1, "arg2": arg2}, headers=self._headers) as response: return await response.json() async def get_endpoint2(self, arg1, arg2, optionalarg1=None, optionalarg2=None): endpoint = "/token" async with self._session.get(self.url + endpoint, params={"arg1": arg1, "arg2": arg2, "optionalarg1": optionalarg1, "optionalarg2": optionalarg2}, headers=self._headers) as response: return await response.json()
这个类支持两种使用方式:
- 显式实例化后手动调用
close()关闭会话:
import asyncio import AsyncWrapper api_key = "my_key" async def main(): client = AsyncWrapper(api_key) response1 = await client.get_endpoint1("argument1", "argument2") print(response1) await client.close() asyncio.run(main())
运行后控制台会输出警告:
Unclosed client session client_session: <aiohttp.client.ClientSession object at 0x0000028D1F0E0390>
- 使用异步上下文管理器(
async with):
import asyncio import AsyncWrapper api_key = "my_key" async def main(): async with AsyncWrapper(api_key) as client: response1 = await client.get_endpoint1("argument1", "argument2") print(response1) asyncio.run(main())
这种方式不会出现任何警告。
差异原因
出现警告的核心原因是:
- 手动调用
close()后,self._session仍然持有对aiohttp.ClientSession实例的引用,直到AsyncWrapper对象被Python垃圾回收器回收。 - aiohttp的
ClientSession在其__del__方法中会检查会话状态,即使你已经调用了close(),只要对象还存在,就可能触发误判的“未关闭会话”警告。 - 而使用
async with时,上下文管理器结束后,AsyncWrapper对象会被立即销毁,self._session的引用也随之被清除,不会等到垃圾回收阶段再触发检查。
解决方法
修改close()方法,在关闭会话后主动解除对ClientSession实例的引用:
async def close(self): await self._session.close() self._session = None # 解除引用,避免垃圾回收时触发警告
这样,当你手动调用close()后,self._session被置为None,ClientSession实例会被及时回收,不会触发aiohttp的未关闭会话警告。
内容的提问来源于stack exchange,提问作者alb2001
相关产品推荐
相关产品推荐

