Skip to content

Allow Responses to be used as Context Managers #393

Closed

Description

@sethmlarson

Requests API allows Responses to be used and closed as a context manager.

We should allow the following use-cases:

async def run_async():
    client = httpx.AsyncClient()
    async with client.get("https://example.com") as resp:
        resp.raise_for_status()

def run_sync():
    client = httpx.Client()
    with client.get("https://example.com") as resp:
        resp.raise_for_status()

See: LoryCS/anido#1

Activity

  1. florimondmanca commented on Sep 27, 2019

    @florimondmanca
    Contributor

    I think this is a good thing beyond Requests compatibility — ensures that all resources are properly closed in case something goes wrong. I'm kind of growing the intuition that ideally we should be following trio's lessons and enforce (async) context manager usage everywhere — on clients, on responses, …

  2. lovelydinosaur commented on Sep 27, 2019

    @lovelydinosaur
    Member

    Okay, so...

    This is relevant only for the streaming case.
    In other cases the response has been closed by the point it's returned from the function.

    There's a question here to be had in that client.get(..., stream=True) is actually fundamentally different to client.get(...) because you really want to be using a context manager for the first case.

    Requests has exactly the same issue, and actually makes it really easy to not close streaming responses. One option that we could do here that's a bit radical would be to diverge from the requests API here, even through stream=True is a fairly widely used case.

    Eg. client.stream() could return a context managed response. (It might have a default method='get' that could be overidden.)

    One other bugbear to be wary of here is that "stream" in this context specifically means "stream the response", not "stream the request" (which yes, we support, but that's different), but the naming isn't very clear about the expectation.

  3. gdhameeja commented on Oct 3, 2019

    @gdhameeja
    Contributor

    I'd like to work on this.

  4. lovelydinosaur commented on Oct 4, 2019

    @lovelydinosaur
    Member

    Main question here is where we want to go with the API...

    • Add a .stream(method='GET') method which is always what we use for streaming cases, so that we can ensure that they're always context-manged.
    • Stay in line with requests and just use stream=True on the existing methods. Keeps the API the same, but really easy to fail to close a response, which isn't awful but will leave the connection hanging.
  5. sethmlarson commented on Oct 4, 2019

    @sethmlarson
    ContributorAuthor

    Maybe we can put a warning on the Responses garbage collector that it wasn't closed properly, similar to files?

  6. deleted a comment from on Oct 4, 2019
  7. lovelydinosaur commented on Nov 27, 2019

    @lovelydinosaur
    Member

    Closing this. Evidentally outdated somewhere along the line - we do support responses as context managers. 👍

  8. florimondmanca commented on Nov 29, 2019

    @florimondmanca
    Contributor

    @tomchristie Do we? The Response class doesn't implement __aenter__()/__aexit__(), so this might have been an oversight?

    # client.py
    import asyncio
    import httpx
    
    async def main():
        url = "http://localhost:8000"
        async with httpx.get(url) as resp:
            print(resp)
    
    asyncio.run(main())
    $ python debug/client.py
    debug/client.py:8: RuntimeWarning: coroutine 'get' was never awaited
      async with httpx.get(url) as resp:
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    Traceback (most recent call last):
      File "debug/client.py", line 12, in <module>
        asyncio.run(main())
      File "/Users/florimond/.pyenv/versions/3.8.0/lib/python3.8/asyncio/runners.py", line 43, in run
        return loop.run_until_complete(main)
      File "/Users/florimond/.pyenv/versions/3.8.0/lib/python3.8/asyncio/base_events.py", line 608, in run_until_complete
        return future.result()
      File "debug/client.py", line 8, in main
        async with httpx.get(url) as resp:
    AttributeError: __aexit__
  9. added this to the v1.0 milestone on Nov 30, 2019
  10. lovelydinosaur commented on Dec 9, 2019

    @lovelydinosaur
    Member

    Obsoleted by #600

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    good first issueGood for newcomersrequests-compatIssues related to Requests backwards compatibility

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions