Switch event hooks to also run on redirects. (#1806)

* Switch event hooks to also run on redirects

* Bump coverage

* Add pragma: no cover, because sometime ya just gotta be pragmatic

* Update docs with note about response.read()
This commit is contained in:
Tom Christie 2021-08-18 15:12:39 +01:00 committed by GitHub
parent 4986743b3d
commit 2d9c3580e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 16 deletions

View File

@ -255,12 +255,19 @@ def raise_on_4xx_5xx(response):
client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
```
!!! note
Response event hooks are called before determining if the response body
should be read or not.
If you need access to the response body inside an event hook, you'll
need to call `response.read()`.
The hooks are also allowed to modify `request` and `response` objects.
```python
def add_timestamp(request):
request.headers['x-request-timestamp'] = datetime.now(tz=datetime.utc).isoformat()
client = httpx.Client(event_hooks={'request': [add_timestamp]})
```

View File

@ -886,9 +886,6 @@ class Client(BaseClient):
if not stream:
response.read()
for hook in self._event_hooks["response"]:
hook(response)
return response
except Exception as exc:
@ -907,9 +904,6 @@ class Client(BaseClient):
try:
request = next(auth_flow)
for hook in self._event_hooks["request"]:
hook(request)
while True:
response = self._send_handling_redirects(
request,
@ -947,8 +941,13 @@ class Client(BaseClient):
"Exceeded maximum allowed redirects.", request=request
)
for hook in self._event_hooks["request"]:
hook(request)
response = self._send_single_request(request, timeout)
try:
for hook in self._event_hooks["response"]:
hook(response)
response.history = list(history)
if not response.is_redirect:
@ -1595,12 +1594,9 @@ class AsyncClient(BaseClient):
if not stream:
await response.aread()
for hook in self._event_hooks["response"]:
await hook(response)
return response
except Exception as exc:
except Exception as exc: # pragma: no cover
await response.aclose()
raise exc
@ -1616,9 +1612,6 @@ class AsyncClient(BaseClient):
try:
request = await auth_flow.__anext__()
for hook in self._event_hooks["request"]:
await hook(request)
while True:
response = await self._send_handling_redirects(
request,
@ -1656,8 +1649,14 @@ class AsyncClient(BaseClient):
"Exceeded maximum allowed redirects.", request=request
)
for hook in self._event_hooks["request"]:
await hook(request)
response = await self._send_single_request(request, timeout)
try:
for hook in self._event_hooks["response"]:
await hook(response)
response.history = list(history)
if not response.is_redirect:

View File

@ -117,7 +117,7 @@ async def test_async_event_hooks_raising_exception():
def test_event_hooks_with_redirect():
"""
A redirect request should not trigger a second 'request' event hook.
A redirect request should trigger additional 'request' and 'response' event hooks.
"""
events = []
@ -136,6 +136,21 @@ def test_event_hooks_with_redirect():
http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
assert events == [
{
"event": "request",
"headers": {
"host": "127.0.0.1:8000",
"user-agent": f"python-httpx/{httpx.__version__}",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"connection": "keep-alive",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
},
{
"event": "response",
"headers": {"location": "/", "server": "testserver"},
},
{
"event": "request",
"headers": {
@ -157,7 +172,7 @@ def test_event_hooks_with_redirect():
@pytest.mark.usefixtures("async_environment")
async def test_async_event_hooks_with_redirect():
"""
A redirect request should not trigger a second 'request' event hook.
A redirect request should trigger additional 'request' and 'response' event hooks.
"""
events = []
@ -176,6 +191,21 @@ async def test_async_event_hooks_with_redirect():
await http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
assert events == [
{
"event": "request",
"headers": {
"host": "127.0.0.1:8000",
"user-agent": f"python-httpx/{httpx.__version__}",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"connection": "keep-alive",
"authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
},
},
{
"event": "response",
"headers": {"location": "/", "server": "testserver"},
},
{
"event": "request",
"headers": {