python - 在类 __init__ 上定义 aiohttp ClientSession 以供以后重用
问题描述
我想创建一个持续到程序结束的持久会话,我有以下代码。
import asyncio
import aiohttp
import atexit
class Session:
def __init__(self):
self._session = aiohttp.ClientSession()
atexit.register(self._close_session)
async def get(self, url):
response = await self._session.request("GET", url)
return await response.json()
def _close_session(self):
asyncio.run(self._session.close())
async def pullit():
print(await session.get("https://raw.communitydragon.org/latest/game/data/characters/aatrox/aatrox.bin.json"))
session = Session()
asyncio.run(pullit()) # THIS THROWS: Timeout context manager should be used inside a task
asyncio.get_event_loop().run_until_complete(pullit()) #THIS RUNS OK
这给我抛出了一个异常self._session.request
,Timeout context manager should be used inside a task
我已经搜索了其他答案,但它仍然给出相同的错误。
问题:这个错误的原因是什么?如果我想打开一个持续程序生命周期的会话,并且我需要在一个类中定义它(强制性),那会怎样?
额外:目前我正在使用atexit
程序结束时关闭会话(参考上面的代码),这是一个好方法吗?如果没有,什么是更好的做法
更新:我找到了解决方案,它是使用asyncio.get_event_loop().run_until_complete(...)
,但asyncio.run()
与上面的不一样吗?为什么一个运行没有问题而 3.7+asyncio.run()
不运行?
更新2:我最终得到以下代码:
#runner.py
import aiohttp
import asyncio
class Runner:
def __init__(self, coro):
self.coro = coro
async def run(self):
session = aiohttp.ClientSession()
client.start_session(session)
await self.coro
client.close_session()
await session.close()
def run(coro):
runner = Runner(coro)
return asyncio.run(runner.run())
#client.py
class Client:
def __init__(self):
self._session = None
async def get(self, url):
response = await self._session.request("GET", url)
return await response.json()
def start_session(self, session):
self._session = session
def close_session(self):
self._session = None
from .runner import run
from .client import Client
client = Client()
async def pullit():
print(await client.get("https://raw.communitydragon.org/latest/game/data/characters/aatrox/aatrox.bin.json"))
run(pullit())
好的,这运行了一切,但运行后它会抛出我RuntimeError: Event loop is closed
,我从来没有关闭一个循环。
解决方案
这是我对您的问题的解决方案:
import aiohttp
import asyncio
import atexit
class HTTPClient():
def __init__(self):
self._session = aiohttp.ClientSession()
atexit.register(self._shutdown)
print('session created')
async def request(self, *args, **kwargs):
async with self._session.request(*args, **kwargs) as response:
return (response.status, await response.text())
def _shutdown(self):
asyncio.run(self._session.close())
print('session closed')
async def main():
http_client = HTTPClient()
status, text = await http_client.request(url='http://example.com', method='GET')
print(status)
asyncio.run(main())
另一方面,我想,最好的方法应该是对以下代码的一些包装:
async with aiohttp.ClientSession() as session:
# perform all needed http calls inside
pass
推荐阅读
- java - 无法在java(客户端)的套接字上正确写入数据(类)并在c ++(服务器)上读取它
- asp.net - azure cli 中带有 json 文件的 azure appsettings
- xcode - Xcode 11 b5:首次启动错误消息 - 我应该担心吗?
- android - 从 Facebook 应用拦截链接以打开我的 Android 应用
- git - 防止 Jenkins 在提交时自动构建
- r - 如何将列表的所有对象应用于改变 dplyr tibble 的函数?
- javascript - 如何从js接收来自jsp的参数?
- apple-sign-in - ASAuthorizationController 因现有设置流程而失败
- rest - 如何使用带有邮递员的 REST API 获取 HP ALM 14 登录身份验证令牌
- javascript - 从 xml 字符串创建一个 html 表