Skip to content

Commit

Permalink
Allow Requests.Response to be used as a context manager
Browse files Browse the repository at this point in the history
This saves having to wrap the call to requests with
`contextlib.closing()`, allowing it to be used directly in a
`with` statement, like so:

```
with requests.get('http://httpbin.org/get', stream=True) as r:
    # Do things with the response here.
```

Fixes #4136.
  • Loading branch information
edmorley committed Jun 6, 2017
1 parent 40ce814 commit 4847f5b
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 6 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Expand Up @@ -183,3 +183,4 @@ Patches and Suggestions
- Shmuel Amar (`@shmuelamar <https://github.com/shmuelamar>`_)
- Gary Wu (`@garywu <https://github.com/garywu>`_)
- Ryan Pineo (`@ryanpineo <https://github.com/ryanpineo>`_)
- Ed Morley (`@edmorley <https://github.com/edmorley>`_)
5 changes: 5 additions & 0 deletions HISTORY.rst
Expand Up @@ -6,6 +6,11 @@ Release History
dev
+++

**Improvements**

- ``Response`` is now a context manager, so can be used directly in a `with` statement
without first having to be wrapped by ``contextlib.closing()``.

**Bugfixes**

- Resolve installation failure if multiprocessing is not available
Expand Down
8 changes: 2 additions & 6 deletions docs/user/advanced.rst
Expand Up @@ -301,15 +301,11 @@ release the connection back to the pool unless you consume all the data or call
:meth:`Response.close <requests.Response.close>`. This can lead to
inefficiency with connections. If you find yourself partially reading request
bodies (or not reading them at all) while using ``stream=True``, you should
consider using ``contextlib.closing`` (`documented here`_), like this::
make the request within a ``with`` statement to ensure it's always closed::

from contextlib import closing

with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
with requests.get('http://httpbin.org/get', stream=True) as r:
# Do things with the response here.

.. _`documented here`: http://docs.python.org/2/library/contextlib.html#contextlib.closing

.. _keep-alive:

Keep-Alive
Expand Down
6 changes: 6 additions & 0 deletions requests/models.py
Expand Up @@ -634,6 +634,12 @@ def __init__(self):
#: is a response.
self.request = None

def __enter__(self):
return self

def __exit__(self, *args):
self.close()

def __getstate__(self):
# Consume everything; accessing the content attribute makes
# sure the content has been fully read.
Expand Down
6 changes: 6 additions & 0 deletions tests/test_requests.py
Expand Up @@ -1668,6 +1668,12 @@ def test_response_iter_lines(self, httpbin):
next(it)
assert len(list(it)) == 3

def test_response_context_manager(self, httpbin):
with requests.get(httpbin('stream/4'), stream=True) as response:
assert isinstance(response, requests.Response)

assert response.raw.closed

def test_unconsumed_session_response_closes_connection(self, httpbin):
s = requests.session()

Expand Down

0 comments on commit 4847f5b

Please sign in to comment.