Skip to content

Enforcing AsyncClient as context-managed only? #769

@lovelydinosaur

Description

@lovelydinosaur

This issue isn't neccessarily something that I think we ought to do, or not do. However, since we've got our public API pretty squared away now, and are starting to think about a 1.0 release, it's something that we really ought to discuss, and make sure we're absolutely happy with whatever we land on.

Something that's come up as a possible? point of design contention has been if AsyncClient ought to strictly only provide a context-managed interface, or if we're okay with supporting __init__ and, optionally calling .aclose().

Currently we're supporting either style, and that's working fine for our users, but...

  • If we ever wanted to support background tasks, then structured concurrency means we'd want the client instances to be context-managed, so that we've got well defined lifetimes for any background running tasks.
  • The context managed style also means strictly managed resource managment. Connections have well-scoped lifetimes, rather than "cleanup on interpreter exit".

Do we need to be able to support background tasks. Well, possibly.

We don't strictly need background tasks to support our HTTP/2 implementation, so long as we're using HTTP/1.1 shortish timeouts, on connections that have no activity. (If we're not running a background task on it, then if there's no request/response activity progressing the connection, then we won't respond to HTTP/2 pings, and servers will time us out.)

However, if we did have background tasks then...

  • We could have nicer HTTP/1.1 keep-alive timeouts, where we actually properly expire connections in the background after their timeout period.
  • We could optionally support long-lived HTTP/2 connections, holding onto a connection for longer periods of time in order to provide lower latency when the next request is made.

Also, I've no idea how this ties in with HTTP/3.

Other relevant factors here:

  • It's also possible that some limited kinds of global tasks might at some point be supported by trio [discussion] [potential heresy] global connection pools and global tasks python-trio/hip#125
  • One reason I was initially reticant about strictly enforcing context managed async clients is that I wasn't clear how that'd fit in with the ASGI style, but having put some more thought in there it's clear that it can fit in with ASGI's lifespan messaging. Eg: Support generator function to manage startup/shutdown. Kludex/starlette#799
  • We'd be suggesting a deliberate disparity with the sync case, which is a bit odd. I don't think we'd want to / be able to enforce context managed sync clients. It wouldn't work with WSGI, and anyways we'd like folks to be able to switch over from using requests to using httpx, and we wouldn't be able to justify the constraint. It's also less valuable since we wouldn't be running background tasks in the sync case anyways, so it'd only be relevant wrt. clean connection lifetimes.
  • Simply having an async context managed class doesn't actually tie in fully with "And we'd like a nursery available within this context" See conversation around https://gitter.im/python-trio/general?at=5e20aa33ad195a0f671cc071 - You can do it with some clever metaclassing, or you can potnentially call directly into the nursery __aenter__ and __aexit__, but it might not neccessarily? be the API we'd want to adopt if we wanted a nusery available within AsyncClient. (I think we'd probably? on ok ground here tho' - there's options available.)

I don't have any clear answer on this. What we've got at the moment is working fine for our users, but it's feasible that adopting the more constrained option could prevent issues further down the line?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions