I would like to create a scheduler class that uses aiohttp to make API calls. I tried this:
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()
but this just results in an error: RuntimeError: Session is closed
. A second approach for the __aenter__
function:
async def __aenter__(self):
self.session = aiohttp.ClientSession()
return self
works well. Is this a good construct? It doesn't adhere to examples of how to use aiohttp. Also wondering why the first approach isn't working?
You can't use
with
inside a function and have the context manager remain open, no. Thewith with aiohttp.ClientSession() as session:
block exits as soon as you usereturn
to exit the__aenter__
coroutine!For the specific case, entering a
aiohttp.ClientSession()
context manager does nothing but returnself
. So for that type, just creating the instance and storing it inself.session
, and awaiting onself.session.close()
suffices here, yes.The general pattern for a nested asynchronous context manager is to await the
__aenter__
and__aexit__
methods of a nested async context manager from your own such methods (and perhaps pass along the exception information):Technically speaking, you should first assure that there is an actual
__aexit__
attribute before entering a nested context manager:See the official PEP that added the concept.
You can manage that dependency externally:
When that
async with
chain becomes too ridiculous you can useAsyncExitStack
: