Skip to content

Commit

Permalink
Patchback/backports/3.9/e5beaca052434cbf1c1814170917085eb271ba43/pr 3…
Browse files Browse the repository at this point in the history
…892 implement response check (#6731)

* implement response check (#3892)

* implement response check, #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)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove noqa

Co-authored-by: Samuel Colvin <s@muelcolvin.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored May 3, 2022
1 parent 30d1959 commit 81130a5
Show file tree
Hide file tree
Showing 4 changed files with 77 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
15 changes: 12 additions & 3 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ 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,
read_timeout: Union[float, object] = sentinel,
conn_timeout: Optional[float] = None,
timeout: Union[object, ClientTimeout] = sentinel,
Expand Down Expand Up @@ -380,7 +382,9 @@ 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,
read_until_eof: bool = True,
proxy: Optional[StrOrURL] = None,
proxy_auth: Optional[BasicAuth] = None,
Expand Down Expand Up @@ -645,7 +649,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
48 changes: 48 additions & 0 deletions tests/test_client_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -2309,6 +2309,54 @@ 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 81130a5

Please sign in to comment.