Skip to content

Commit 19fd5e5

Browse files
Normalize generic transport failure messages with safe URL fallback
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 3327024 commit 19fd5e5

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

hyperbrowser/transport/async_transport.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .error_utils import (
1010
extract_error_message,
1111
extract_request_error_context,
12+
format_generic_request_failure_message,
1213
format_request_failure_message,
1314
)
1415

@@ -99,7 +100,8 @@ async def post(
99100
raise
100101
except Exception as e:
101102
raise HyperbrowserError(
102-
f"Request POST {url} failed", original_error=e
103+
format_generic_request_failure_message(method="POST", url=url),
104+
original_error=e,
103105
) from e
104106

105107
async def get(
@@ -123,7 +125,8 @@ async def get(
123125
raise
124126
except Exception as e:
125127
raise HyperbrowserError(
126-
f"Request GET {url} failed", original_error=e
128+
format_generic_request_failure_message(method="GET", url=url),
129+
original_error=e,
127130
) from e
128131

129132
async def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
@@ -141,7 +144,8 @@ async def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
141144
raise
142145
except Exception as e:
143146
raise HyperbrowserError(
144-
f"Request PUT {url} failed", original_error=e
147+
format_generic_request_failure_message(method="PUT", url=url),
148+
original_error=e,
145149
) from e
146150

147151
async def delete(self, url: str) -> APIResponse:
@@ -159,5 +163,6 @@ async def delete(self, url: str) -> APIResponse:
159163
raise
160164
except Exception as e:
161165
raise HyperbrowserError(
162-
f"Request DELETE {url} failed", original_error=e
166+
format_generic_request_failure_message(method="DELETE", url=url),
167+
original_error=e,
163168
) from e

hyperbrowser/transport/error_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,9 @@ def format_request_failure_message(
146146
effective_url = request_url if request_url != "unknown URL" else fallback_url
147147
effective_url = _normalize_request_url(effective_url)
148148
return f"Request {effective_method} {effective_url} failed"
149+
150+
151+
def format_generic_request_failure_message(*, method: str, url: object) -> str:
152+
normalized_method = _normalize_request_method(method)
153+
normalized_url = _normalize_request_url(url)
154+
return f"Request {normalized_method} {normalized_url} failed"

hyperbrowser/transport/sync.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .error_utils import (
1010
extract_error_message,
1111
extract_request_error_context,
12+
format_generic_request_failure_message,
1213
format_request_failure_message,
1314
)
1415

@@ -90,7 +91,8 @@ def post(
9091
raise
9192
except Exception as e:
9293
raise HyperbrowserError(
93-
f"Request POST {url} failed", original_error=e
94+
format_generic_request_failure_message(method="POST", url=url),
95+
original_error=e,
9496
) from e
9597

9698
def get(
@@ -114,7 +116,8 @@ def get(
114116
raise
115117
except Exception as e:
116118
raise HyperbrowserError(
117-
f"Request GET {url} failed", original_error=e
119+
format_generic_request_failure_message(method="GET", url=url),
120+
original_error=e,
118121
) from e
119122

120123
def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
@@ -132,7 +135,8 @@ def put(self, url: str, data: Optional[dict] = None) -> APIResponse:
132135
raise
133136
except Exception as e:
134137
raise HyperbrowserError(
135-
f"Request PUT {url} failed", original_error=e
138+
format_generic_request_failure_message(method="PUT", url=url),
139+
original_error=e,
136140
) from e
137141

138142
def delete(self, url: str) -> APIResponse:
@@ -150,5 +154,6 @@ def delete(self, url: str) -> APIResponse:
150154
raise
151155
except Exception as e:
152156
raise HyperbrowserError(
153-
f"Request DELETE {url} failed", original_error=e
157+
format_generic_request_failure_message(method="DELETE", url=url),
158+
original_error=e,
154159
) from e

tests/test_transport_error_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from hyperbrowser.transport.error_utils import (
44
extract_error_message,
55
extract_request_error_context,
6+
format_generic_request_failure_message,
67
format_request_failure_message,
78
)
89

@@ -234,6 +235,24 @@ def test_format_request_failure_message_rejects_overlong_fallback_methods():
234235
assert message == "Request UNKNOWN https://example.com/fallback failed"
235236

236237

238+
def test_format_generic_request_failure_message_normalizes_invalid_url_objects():
239+
message = format_generic_request_failure_message(
240+
method="GET",
241+
url=object(),
242+
)
243+
244+
assert message == "Request GET unknown URL failed"
245+
246+
247+
def test_format_generic_request_failure_message_normalizes_invalid_method_values():
248+
message = format_generic_request_failure_message(
249+
method="GET /invalid",
250+
url="https://example.com/path",
251+
)
252+
253+
assert message == "Request UNKNOWN https://example.com/path failed"
254+
255+
237256
def test_format_request_failure_message_truncates_very_long_fallback_urls():
238257
very_long_url = "https://example.com/" + ("a" * 1200)
239258
message = format_request_failure_message(

tests/test_transport_response_handling.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,22 @@ def failing_delete(*args, **kwargs):
331331
transport.close()
332332

333333

334+
def test_sync_transport_wraps_unexpected_errors_with_invalid_url_fallback():
335+
transport = SyncTransport(api_key="test-key")
336+
original_get = transport.client.get
337+
338+
def failing_get(*args, **kwargs):
339+
raise RuntimeError("boom")
340+
341+
transport.client.get = failing_get # type: ignore[assignment]
342+
try:
343+
with pytest.raises(HyperbrowserError, match="Request GET unknown URL failed"):
344+
transport.get(None) # type: ignore[arg-type]
345+
finally:
346+
transport.client.get = original_get # type: ignore[assignment]
347+
transport.close()
348+
349+
334350
def test_async_transport_put_wraps_unexpected_errors_with_url_context():
335351
async def run() -> None:
336352
transport = AsyncTransport(api_key="test-key")
@@ -353,6 +369,27 @@ async def failing_put(*args, **kwargs):
353369
asyncio.run(run())
354370

355371

372+
def test_async_transport_wraps_unexpected_errors_with_invalid_url_fallback():
373+
async def run() -> None:
374+
transport = AsyncTransport(api_key="test-key")
375+
original_put = transport.client.put
376+
377+
async def failing_put(*args, **kwargs):
378+
raise RuntimeError("boom")
379+
380+
transport.client.put = failing_put # type: ignore[assignment]
381+
try:
382+
with pytest.raises(
383+
HyperbrowserError, match="Request PUT unknown URL failed"
384+
):
385+
await transport.put(None) # type: ignore[arg-type]
386+
finally:
387+
transport.client.put = original_put # type: ignore[assignment]
388+
await transport.close()
389+
390+
asyncio.run(run())
391+
392+
356393
def test_sync_transport_request_error_without_request_uses_fallback_url():
357394
transport = SyncTransport(api_key="test-key")
358395
original_get = transport.client.get

0 commit comments

Comments
 (0)