在 asyncio 中混合异步上下文管理器和直接等待
Mixinig async context manager and straight await in asyncio
如何搭配
async with api.open() as o:
...
和
o = await api.open()
在一个函数中?
因为首先需要 __aenter__
和 __aexit__
的对象,但第二次需要 __await__
,它应该是没有 await
的生成器。
我的尝试是:
def AsyncContext(aenter, aexit):
class AsyncContextClass:
async def __aenter__(self):
return await aenter()
async def __aexit__(self, *args):
return await aexit(*args)
def __await__(self):
return (yield from aenter())
return AsyncContextClass()
但是如果 aenter
定义为 async def
(TypeError: cannot 'yield from' a coroutine object in a non-coroutine generator
).
则失败 __await__
它与 aenter
的 @asyncio.coroutine
装饰器配合使用效果很好,但这很“脏”。
您可以从 class 的 __await__
return __aenter__
的 __await__
:
# vim: tabstop=4 expandtab
import asyncio
class Test(object):
async def __aenter__(self):
print("enter")
async def __aexit__(self, *args):
print("exit")
def __await__(self):
return self.__aenter__().__await__()
async def run():
async with Test():
print("hello")
await Test()
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
输出:
enter
hello
exit
enter
在 python 3.8.5 中你可以这样做
from httpx import AsyncClient
from asyncio import run
async def main():
api_base_url = 'https://google.com/'
api_session_build = AsyncClient(base_url=api_base_url)
# Enter Async Context manager
apisession = await api_session_build.__aenter__()
# Do stuff
print( await apisession.get('/photos/about/'))
# Exit Async Context Manager
await apisession.__aexit__()
run(main())
如何搭配
async with api.open() as o:
...
和
o = await api.open()
在一个函数中?
因为首先需要 __aenter__
和 __aexit__
的对象,但第二次需要 __await__
,它应该是没有 await
的生成器。
我的尝试是:
def AsyncContext(aenter, aexit):
class AsyncContextClass:
async def __aenter__(self):
return await aenter()
async def __aexit__(self, *args):
return await aexit(*args)
def __await__(self):
return (yield from aenter())
return AsyncContextClass()
但是如果 aenter
定义为 async def
(TypeError: cannot 'yield from' a coroutine object in a non-coroutine generator
).
__await__
它与 aenter
的 @asyncio.coroutine
装饰器配合使用效果很好,但这很“脏”。
您可以从 class 的 __await__
return __aenter__
的 __await__
:
# vim: tabstop=4 expandtab
import asyncio
class Test(object):
async def __aenter__(self):
print("enter")
async def __aexit__(self, *args):
print("exit")
def __await__(self):
return self.__aenter__().__await__()
async def run():
async with Test():
print("hello")
await Test()
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
输出:
enter
hello
exit
enter
在 python 3.8.5 中你可以这样做
from httpx import AsyncClient
from asyncio import run
async def main():
api_base_url = 'https://google.com/'
api_session_build = AsyncClient(base_url=api_base_url)
# Enter Async Context manager
apisession = await api_session_build.__aenter__()
# Do stuff
print( await apisession.get('/photos/about/'))
# Exit Async Context Manager
await apisession.__aexit__()
run(main())