Skip to content

Commit 57cc1ad

Browse files
authored
Add raise_for_status parameter to Session and AsyncSession (#651)
* Add raise_for_status parameter to Session and AsyncSession - Add raise_for_status parameter (default: False) to both Session and AsyncSession - Automatically raises HTTPError for 4xx/5xx status codes when enabled - Similar to aiohttp's raise_for_status parameter - Add 4 unit tests (2 sync, 2 async) to verify functionality - Maintains 100% backward compatibility with default False value * Fix linting: break long docstring lines to <= 88 chars * Fix linting: line length and assert statements
1 parent e366cb3 commit 57cc1ad

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

curl_cffi/requests/session.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class BaseSessionParams(Generic[R], TypedDict, total=False):
8080
cert: Optional[Union[str, tuple[str, str]]]
8181
response_class: Optional[type[R]]
8282
discard_cookies: bool
83+
raise_for_status: bool
8384

8485
class StreamRequestParams(TypedDict, total=False):
8586
params: Optional[Union[dict, list, tuple]]
@@ -188,6 +189,7 @@ def __init__(
188189
cert: Optional[Union[str, tuple[str, str]]] = None,
189190
response_class: Optional[type[R]] = None,
190191
discard_cookies: bool = False,
192+
raise_for_status: bool = False,
191193
):
192194
self.headers = Headers(headers)
193195
self._cookies = Cookies(cookies) # guarded by @property
@@ -220,6 +222,7 @@ def __init__(
220222
)
221223
self.response_class = response_class or Response
222224
self.discard_cookies = discard_cookies
225+
self.raise_for_status = raise_for_status
223226

224227
if proxy and proxies:
225228
raise TypeError("Cannot specify both 'proxy' and 'proxies'")
@@ -383,6 +386,8 @@ def __init__(
383386
automatic detection.
384387
cert: a tuple of (cert, key) filenames for client cert.
385388
response_class: A customized subtype of ``Response`` to use.
389+
raise_for_status: automatically raise an HTTPError for 4xx and 5xx
390+
status codes.
386391
387392
Notes:
388393
This class can be used as a context manager.
@@ -628,6 +633,8 @@ def cleanup(fut):
628633
rsp.stream_task = stream_task
629634
rsp.quit_now = quit_now
630635
rsp.queue = q
636+
if self.raise_for_status:
637+
rsp.raise_for_status()
631638
return rsp
632639
else:
633640
try:
@@ -653,6 +660,8 @@ def cleanup(fut):
653660
c, buffer, header_buffer, default_encoding, discard_cookies
654661
)
655662
rsp.request = req
663+
if self.raise_for_status:
664+
rsp.raise_for_status()
656665
return rsp
657666
finally:
658667
c.reset()
@@ -730,6 +739,8 @@ def __init__(
730739
automatic detection.
731740
cert: a tuple of (cert, key) filenames for client cert.
732741
response_class: A customized subtype of ``Response`` to use.
742+
raise_for_status: automatically raise an HTTPError for 4xx and 5xx
743+
status codes.
733744
734745
Notes:
735746
This class can be used as a context manager, and it's recommended to use via
@@ -1066,6 +1077,8 @@ def cleanup(fut):
10661077
rsp.astream_task = stream_task
10671078
rsp.quit_now = quit_now
10681079
rsp.queue = q
1080+
if self.raise_for_status:
1081+
rsp.raise_for_status()
10691082
return rsp
10701083
else:
10711084
try:
@@ -1083,6 +1096,8 @@ def cleanup(fut):
10831096
curl, buffer, header_buffer, default_encoding, discard_cookies
10841097
)
10851098
rsp.request = req
1099+
if self.raise_for_status:
1100+
rsp.raise_for_status()
10861101
return rsp
10871102
finally:
10881103
self.release_curl(curl)

tests/unittest/test_async_session.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,25 @@ async def test_stream_atext(server):
457457
text = await r.atext()
458458
chunks = text.split("\n")
459459
assert len(chunks) == 20
460+
461+
462+
async def test_async_session_auto_raise_for_status_enabled(server):
463+
"""Test that AsyncSession automatically raises HTTPError for error status codes
464+
when raise_for_status=True"""
465+
from curl_cffi.requests.exceptions import HTTPError
466+
467+
async with AsyncSession(raise_for_status=True) as s:
468+
try:
469+
await s.get(str(server.url.copy_with(path="/status/404")))
470+
raise AssertionError("Should have raised HTTPError for 404")
471+
except HTTPError as e:
472+
assert e.response.status_code == 404 # type: ignore
473+
474+
475+
async def test_async_session_auto_raise_for_status_disabled(server):
476+
"""Test that AsyncSession does NOT raise HTTPError when raise_for_status=False
477+
(default)"""
478+
async with AsyncSession(raise_for_status=False) as s:
479+
r = await s.get(str(server.url.copy_with(path="/status/404")))
480+
assert r.status_code == 404
481+
# Should not raise an exception

tests/unittest/test_requests.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,3 +982,23 @@ def test_response_ip_and_port(server):
982982
def test_http_version(server):
983983
r = requests.get(str(server.url), http_version="v1")
984984
assert r.status_code == 200
985+
986+
987+
def test_session_auto_raise_for_status_enabled(server):
988+
"""Test that Session automatically raises HTTPError for error status codes
989+
when raise_for_status=True"""
990+
s = requests.Session(raise_for_status=True)
991+
try:
992+
s.get(str(server.url.copy_with(path="/status/404")))
993+
raise AssertionError("Should have raised HTTPError for 404")
994+
except HTTPError as e:
995+
assert e.response.status_code == 404 # type: ignore
996+
997+
998+
def test_session_auto_raise_for_status_disabled(server):
999+
"""Test that Session does NOT raise HTTPError when raise_for_status=False
1000+
(default)"""
1001+
s = requests.Session(raise_for_status=False)
1002+
r = s.get(str(server.url.copy_with(path="/status/404")))
1003+
assert r.status_code == 404
1004+
# Should not raise an exception

0 commit comments

Comments
 (0)