Search code examples
pythonasync-awaitaiohttpsanic

Session reusing in aiohhttp


I try to reuse HTTP-session as aiohttp docs advice

Don’t create a session per request. Most likely you need a session per application which performs all requests altogether.

But usual pattern which I use with requests lib doesn`t work:

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

async def get_u(self, id):
    async with self.session.get('url') as resp:
        json_resp = await resp.json()

        return json_resp.get('data', {})

Then I try to

await client.get_u(1)

I got error

RuntimeError: Timeout context manager should be used inside a task

Any workarounds with async_timeout didn't help.

Another way is working:

async def get_u(self, id):
    async with aiohttp.ClientSession() as session:
        with async_timeout.timeout(3):
            async with session.get('url') as resp:
                json_resp = await resp.json()
                return json_resp.get('data', {})

But it seems like creating session per request.
So my question: how to properly reuse aiohttp-session?

UPD: minimal working example. Sanic application with following view

import aiohttp
from sanic.views import HTTPMethodView


class Client:
    def __init__(self):
        self.session = aiohttp.ClientSession()
        self.url = 'https://jsonplaceholder.typicode.com/todos/1'

    async def get(self):
        async with self.session.get(self.url) as resp:
            json_resp = await resp.json()

            return json_resp


client = Client()


class ExView(HTTPMethodView):
    async def get(self, request):
        todo = await client.get()
        print(todo)

Solution

  • I had the same error. The solution for me was initializing the client within an async function. EG:

    class SearchClient(object):
    
        def __init__(self, search_url: str, api_key: str):
            self.search_url = search_url
            self.api_key = api_key
            self.session = None
    
        async def _get(self, url, attempt=1):
            if self.session is None:
                self.session = aiohttp.ClientSession(raise_for_status=True)
    
            headers = {
                'Content-Type': 'application/json',
                'api-key': self.api_key
            }
            logger.info("Running Search: {}".format(url))
            try:
                with timeout(60):
                    async with self.session.get(url, headers=headers) as response:
                        results = await response.json()
                        return results