The expression TestApi()
on a line by itself creates a TestApi
object and immediately throws it away. aiohttp
complaints that the session was never closed (either by leaving an async with
block or with an explicit call to close()
), but even without the warning it doesn't make sense not to assign the API object to a variable where it will be actually used.
To reuse the session, your code needs to have access to the session, or to an object that holds it:
async def fetch(url):
async with aiohttp.request('GET', url) as resp:
resp.raise_for_status()
return await resp.read()
async def main():
url1_data, url2_data = asyncio.gather(
fetch('http://url1'), fetch('http://url2'))
url3_data, url4_data = asyncio.gather(
fetch('http://url3'), fetch('http://url4'))
One option is to add a session
parameter to fetch
(and other functions) and consistently call it with a session created in main()
. A better option is to create an API class and convert the global functions like fetch
to methods:
class Http:
async def __aenter__(self):
self._session = aiohttp.ClientSession()
return self
async def __aexit__(self, *err):
await self._session.close()
self._session = None
async def fetch(self, url):
async with self._session.get(url) as resp:
resp.raise_for_status()
return await resp.read()
main()
can still exist as a function, but it can consistently use the object that holds the session:
async def main():
async with Http() as http:
url1_data, url2_data = await asyncio.gather(
http.fetch('http://url1'), http.fetch('http://url2'))
url3_data, url4_data = await asyncio.gather(
http.fetch('http://url3'), http.fetch('http://url4'))
In the above code, the async with
statement is used to ensure that the session is closed whenever the scope is left.