简体   繁体   中英

aiohttp how to save a persistent ClientSession in a class?

I'm writing a class that will do http requests using aiohttp. According to the docs I should not to create a ClientSession per request, so I want to reuse the same session.

code:

class TestApi:
   def __init__(self):
      self.session = aiohttp.ClientSession()

   # async defs methods from here 

When doing

TestApi()

I get the error: Unclosed client session .

What is the solution to persist the ClientSession object?

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.

Actually, I don't see anything fundamentally wrong with your code.

Except, when your TestApi object is destroyed, likely when your program finishes, you will need to call the close function. Otherwise you get this warning.

(And that is a coroutine that needs to be awaited: https://docs.aiohttp.org/en/stable/client_reference.html )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM