Skip to content

Commit 5f296ec

Browse files
Handle request errors without request objects safely
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 02a934d commit 5f296ec

File tree

4 files changed

+44
-6
lines changed

4 files changed

+44
-6
lines changed

hyperbrowser/transport/async_transport.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from hyperbrowser.header_utils import normalize_headers
77
from hyperbrowser.version import __version__
88
from .base import APIResponse, AsyncTransportStrategy
9-
from .error_utils import extract_error_message
9+
from .error_utils import extract_error_message, extract_request_error_context
1010

1111

1212
class AsyncTransport(AsyncTransportStrategy):
@@ -67,8 +67,7 @@ async def _handle_response(self, response: httpx.Response) -> APIResponse:
6767
original_error=e,
6868
)
6969
except httpx.RequestError as e:
70-
request_method = e.request.method if e.request is not None else "UNKNOWN"
71-
request_url = str(e.request.url) if e.request is not None else "unknown URL"
70+
request_method, request_url = extract_request_error_context(e)
7271
raise HyperbrowserError(
7372
f"Request {request_method} {request_url} failed", original_error=e
7473
)

hyperbrowser/transport/error_utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,13 @@ def extract_error_message(response: httpx.Response, fallback_error: Exception) -
3333
if isinstance(error_data, str):
3434
return error_data
3535
return _stringify_error_value(response.text or str(fallback_error))
36+
37+
38+
def extract_request_error_context(error: httpx.RequestError) -> tuple[str, str]:
39+
try:
40+
request = error.request
41+
except RuntimeError:
42+
request = None
43+
if request is None:
44+
return "UNKNOWN", "unknown URL"
45+
return request.method, str(request.url)

hyperbrowser/transport/sync.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from hyperbrowser.header_utils import normalize_headers
77
from hyperbrowser.version import __version__
88
from .base import APIResponse, SyncTransportStrategy
9-
from .error_utils import extract_error_message
9+
from .error_utils import extract_error_message, extract_request_error_context
1010

1111

1212
class SyncTransport(SyncTransportStrategy):
@@ -55,8 +55,7 @@ def _handle_response(self, response: httpx.Response) -> APIResponse:
5555
original_error=e,
5656
)
5757
except httpx.RequestError as e:
58-
request_method = e.request.method if e.request is not None else "UNKNOWN"
59-
request_url = str(e.request.url) if e.request is not None else "unknown URL"
58+
request_method, request_url = extract_request_error_context(e)
6059
raise HyperbrowserError(
6160
f"Request {request_method} {request_url} failed", original_error=e
6261
)

tests/test_transport_response_handling.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ def raise_for_status(self) -> None:
2121
raise httpx.RequestError("network down", request=self._request)
2222

2323

24+
class _RequestErrorNoRequestResponse:
25+
def raise_for_status(self) -> None:
26+
raise httpx.RequestError("network down")
27+
28+
2429
def test_sync_handle_response_with_non_json_success_body_returns_status_only():
2530
transport = SyncTransport(api_key="test-key")
2631
try:
@@ -81,6 +86,31 @@ async def run() -> None:
8186
asyncio.run(run())
8287

8388

89+
def test_sync_handle_response_with_request_error_without_request_context():
90+
transport = SyncTransport(api_key="test-key")
91+
try:
92+
with pytest.raises(
93+
HyperbrowserError, match="Request UNKNOWN unknown URL failed"
94+
):
95+
transport._handle_response(_RequestErrorNoRequestResponse())
96+
finally:
97+
transport.close()
98+
99+
100+
def test_async_handle_response_with_request_error_without_request_context():
101+
async def run() -> None:
102+
transport = AsyncTransport(api_key="test-key")
103+
try:
104+
with pytest.raises(
105+
HyperbrowserError, match="Request UNKNOWN unknown URL failed"
106+
):
107+
await transport._handle_response(_RequestErrorNoRequestResponse())
108+
finally:
109+
await transport.close()
110+
111+
asyncio.run(run())
112+
113+
84114
def test_sync_handle_response_with_error_and_non_json_body_raises_hyperbrowser_error():
85115
transport = SyncTransport(api_key="test-key")
86116
try:

0 commit comments

Comments
 (0)