Skip to content

Commit

Permalink
implement response check (aio-libs#3892)
Browse files Browse the repository at this point in the history
* implement response check, aio-libs#3864

* switch to raise_for_status coroutine

* fix linting

* revert name corrections

* fix types, add request tests

* change and docs

* allow non callable truey raise_for_status

(cherry picked from commit e5beaca)
  • Loading branch information
samuelcolvin authored and arjd committed May 2, 2022
1 parent 30d1959 commit 15e30be
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/3892.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
allow ``raise_for_status`` to be a coroutine
11 changes: 8 additions & 3 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def __init__(
version: HttpVersion = http.HttpVersion11,
cookie_jar: Optional[AbstractCookieJar] = None,
connector_owner: bool = True,
raise_for_status: bool = False,
raise_for_status: Union[bool, Callable[[ClientResponse], Awaitable[None]]] = False, # noqa
read_timeout: Union[float, object] = sentinel,
conn_timeout: Optional[float] = None,
timeout: Union[object, ClientTimeout] = sentinel,
Expand Down Expand Up @@ -380,7 +380,7 @@ async def _request(
compress: Optional[str] = None,
chunked: Optional[bool] = None,
expect100: bool = False,
raise_for_status: Optional[bool] = None,
raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] = None, # noqa
read_until_eof: bool = True,
proxy: Optional[StrOrURL] = None,
proxy_auth: Optional[BasicAuth] = None,
Expand Down Expand Up @@ -645,7 +645,12 @@ async def _request(
# check response status
if raise_for_status is None:
raise_for_status = self._raise_for_status
if raise_for_status:

if raise_for_status is None:
pass
elif callable(raise_for_status):
await raise_for_status(resp)
elif raise_for_status:
resp.raise_for_status()

# register connection
Expand Down
16 changes: 16 additions & 0 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ The client session supports the context manager protocol for self closing.
requests where you need to handle responses with status 400 or
higher.

You can also provide a coroutine which takes the response as an
argument and can raise an exception based on custom logic, e.g.::

async def custom_check(response):
if response.status not in {201, 202}:
raise RuntimeError('expected either 201 or 202')
text = await response.text()
if 'apple pie' not in text:
raise RuntimeError('I wanted to see "apple pie" in response')

client_session = aiohttp.ClientSession(raise_for_status=custom_check)
...

As with boolean values, you're free to set this on the session and/or
overwrite it on a per-request basis.

:param timeout: a :class:`ClientTimeout` settings structure, 300 seconds (5min)
total timeout by default.

Expand Down
50 changes: 50 additions & 0 deletions tests/test_client_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -2309,6 +2309,56 @@ async def handler(request):
assert False, "never executed" # pragma: no cover


async def test_session_raise_for_status_coro(aiohttp_client) -> None:

async def handle(request):
return web.Response(text='ok')

app = web.Application()
app.router.add_route('GET', '/', handle)

raise_for_status_called = 0

async def custom_r4s(response):
nonlocal raise_for_status_called
raise_for_status_called += 1
assert response.status == 200
assert response.request_info.method == 'GET'

client = await aiohttp_client(app, raise_for_status=custom_r4s)
await client.get('/')
assert raise_for_status_called == 1
await client.get('/', raise_for_status=True)
assert raise_for_status_called == 1 # custom_r4s not called again
await client.get('/', raise_for_status=False)
assert raise_for_status_called == 1 # custom_r4s not called again


async def test_request_raise_for_status_coro(aiohttp_client) -> None:

async def handle(request):
return web.Response(text='ok')

app = web.Application()
app.router.add_route('GET', '/', handle)

raise_for_status_called = 0

async def custom_r4s(response):
nonlocal raise_for_status_called
raise_for_status_called += 1
assert response.status == 200
assert response.request_info.method == 'GET'

client = await aiohttp_client(app)
await client.get('/', raise_for_status=custom_r4s)
assert raise_for_status_called == 1
await client.get('/', raise_for_status=True)
assert raise_for_status_called == 1 # custom_r4s not called again
await client.get('/', raise_for_status=False)
assert raise_for_status_called == 1 # custom_r4s not called again


async def test_invalid_idna() -> None:
session = aiohttp.ClientSession()
try:
Expand Down

0 comments on commit 15e30be

Please sign in to comment.