7

My question is about the right way of making response in aiohttp

Official aiohttp documentation gives us the example of making an async query:

session = aiohttp.ClientSession()

async with session.get('http://httpbin.org/get') as resp:
    print(resp.status)
    print(await resp.text())

await session.close()

I cannot understand, why is the context manager here. All i have found is that __aexit__() method awaits resp.release() method. But the documentation also tells that awaiting resp.release() is not necessary at general.

That all really confuses me.

Why should i do that way if i find the code below more readable and not so nested?

session = aiohttp.ClientSession()

resp = await session.get('http://httpbin.org/get')
print(resp.status)
print(await resp.text())

# I finally have not get the essence of this method.
# I've tried both using and not using this method in my code,
# I've not found any difference in behaviour.
# await resp.release()

await session.close()

I have dug into aiohttp.ClientSession and its context manager sources, but i have not found anything that could clarify the situation.

In the end, my question: what's the difference?

Fedor Soldatkin
  • 1,133
  • 8
  • 23
  • If you’re running a script that doesn’t make a lot of requests and exits pretty quickly, you probably won’t notice a difference. If it’s a long-lived process and makes a lot of requests, releasing the response probably helps limit the resources needed. Without looking at the source, I’d guess that `__aenter__` includes a try/finally block to make sure those resources get freed up even if there’s an uncaught exception somewhere. – dirn Dec 20 '20 at 13:29
  • The difference is that ``async with`` calls ``resp.release()`` promptly, which is not necessary *but advisable*. Just because you do *not have to* do something doesn't mean you *should not* do it. – MisterMiyagi Dec 20 '20 at 13:41
  • @MisterMiyagi I would argue that the aiohttp [docs](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientResponse.release) are confusing in this regard. It literally says in the first sentence of the `release()` docs that it is not required to call that function. It further explains that the connection is closed regardless of whether the payload is fully read or even partially read, without explaining how the latter even works. Possible adverse consequences of not invoking `release()` are omitted entirely. I completely understand the OP's confusion. – user4815162342 Dec 20 '20 at 15:11
  • 2
    @user4815162342 I fully understand the confusion, and do not intend to doubt the validity of the question. Above comment is merely what, after a cursory glance at the code, I think would basically be the proper answer after a proper dig through the code. – MisterMiyagi Dec 20 '20 at 16:01

1 Answers1

2

Explicitly managing a response via async with, is not necessary but advisable. The purpose of async with for response objects is to safely and promptly release resources used by the response (via a call to resp.release()). That is, even if an error occurs the resources are freed and available for further requests/responses.

Otherwise, aiohttp will also release the response resources but without guarantee of promptness. The worst case is that this is delayed for an arbitrary time, i.e. up to the end of the application and timeout of external resources (such as sockets).


The difference is not noticeable if no errors occur (in which case aiohttp cleans up unused resources) and/or if the application is short (in which case there are enough resources to not need re-use). However, since errors may occur unexpectedly and aiohttp is designed for many requests/responses, it is advisable to always default to prompt cleanup via async with.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119