使用aiohttp嵌套“与...同步”

use*_*984 3 python python-asyncio aiohttp

我想创建一个使用aiohttp进行API调用的调度程序类。我尝试了这个:

import asyncio
import aiohttp

class MySession:
    def __init__(self):
        self.session = None

    async def __aenter__(self):
        async with aiohttp.ClientSession() as session:
            self.session = session
            return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()

async def method1():
    async with MySession() as s:
        async with s.session.get("https://www.google.com") as resp:
            if resp.status == 200:
                print("successful call!")

loop = asyncio.get_event_loop()
loop.run_until_complete(method1())
loop.close()
Run Code Online (Sandbox Code Playgroud)

但这只会导致错误:RuntimeError: Session is closed。该__aenter__功能的第二种方法:

    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self
Run Code Online (Sandbox Code Playgroud)

效果很好。这是一个好结构吗?它不遵循如何使用aiohttp的示例。还想知道为什么第一种方法不起作用?

Mar*_*ers 5

您不能with在函数内部使用并让上下文管理器保持打开状态,否。with with aiohttp.ClientSession() as session:一旦return退出__aenter__协程,该块就会退出!

对于特定情况,进入aiohttp.ClientSession()上下文管理器只会执行returnself。因此,对于该类型,只需创建实例并将其存储在中self.session,然后在self.session.close()这里等待就足够了,是的。

一般的嵌套的异步上下文管理模式是等待__aenter____aexit__从自己的这些方法的嵌套的异步上下文管理的方法(也许是沿着异常信息传递):

class MySession:
    def __init__(self):
        self.session = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        await self.session.__aenter__()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            return await self.session.__aexit__(exc_type, exc_val, exc_tb)
Run Code Online (Sandbox Code Playgroud)

从技术上讲,__aexit__在进入嵌套上下文管理器之前,您首先应确保存在一个实际属性:

class MySession:
    def __init__(self):
        self.session = None
        self._session_aexit = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        self._session_aexit = type(self.session).__aexit__
        await self.session.__aenter__()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            return await self._session_aexit.__aexit__(
                self.session, exc_type, exc_val, exc_tb)
Run Code Online (Sandbox Code Playgroud)

请参阅添加了该概念官方PEP