Skip to content

Commit 57f0a74

Browse files
Harden transport URL fallback normalization for booleans
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 1ac2a7a commit 57f0a74

File tree

4 files changed

+96
-1
lines changed

4 files changed

+96
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ Polling timeouts and repeated polling failures are surfaced as:
187187

188188
`HyperbrowserPollingError` also covers stalled pagination (no page-batch progress during result collection).
189189
Transport-level request failures include HTTP method + URL context in error messages.
190-
URL-like fallback objects are stringified for transport diagnostics; missing/malformed/sentinel URL inputs (for example `None`, `null`/`undefined`/`nan`, or numeric-like values such as `123`/`1.5`/`1e6`) are normalized to `unknown URL`.
190+
URL-like fallback objects are stringified for transport diagnostics; missing/malformed/sentinel URL inputs (for example `None`, booleans, `null`/`undefined`/`nan`, or numeric-like values such as `123`/`1.5`/`1e6`) are normalized to `unknown URL`.
191191

192192
```python
193193
from hyperbrowser import Hyperbrowser

hyperbrowser/transport/error_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ def _normalize_request_method(method: Any) -> str:
3939
def _normalize_request_url(url: Any) -> str:
4040
if url is None:
4141
return "unknown URL"
42+
if isinstance(url, bool):
43+
return "unknown URL"
4244
raw_url = url
4345
if not isinstance(raw_url, str):
4446
try:

tests/test_transport_error_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,16 @@ def test_format_request_failure_message_normalizes_numeric_fallback_url_values()
256256
assert message == "Request GET unknown URL failed"
257257

258258

259+
def test_format_request_failure_message_normalizes_boolean_fallback_url_values():
260+
message = format_request_failure_message(
261+
httpx.RequestError("network down"),
262+
fallback_method="GET",
263+
fallback_url=True,
264+
)
265+
266+
assert message == "Request GET unknown URL failed"
267+
268+
259269
@pytest.mark.parametrize(
260270
"sentinel_url",
261271
[
@@ -324,6 +334,15 @@ def test_format_generic_request_failure_message_normalizes_numeric_url_values():
324334
assert message == "Request GET unknown URL failed"
325335

326336

337+
def test_format_generic_request_failure_message_normalizes_boolean_url_values():
338+
message = format_generic_request_failure_message(
339+
method="GET",
340+
url=False,
341+
)
342+
343+
assert message == "Request GET unknown URL failed"
344+
345+
327346
@pytest.mark.parametrize(
328347
"sentinel_url",
329348
[

tests/test_transport_response_handling.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,22 @@ def failing_get(*args, **kwargs):
394394
transport.close()
395395

396396

397+
def test_sync_transport_wraps_unexpected_errors_with_boolean_url_fallback():
398+
transport = SyncTransport(api_key="test-key")
399+
original_get = transport.client.get
400+
401+
def failing_get(*args, **kwargs):
402+
raise RuntimeError("boom")
403+
404+
transport.client.get = failing_get # type: ignore[assignment]
405+
try:
406+
with pytest.raises(HyperbrowserError, match="Request GET unknown URL failed"):
407+
transport.get(True) # type: ignore[arg-type]
408+
finally:
409+
transport.client.get = original_get # type: ignore[assignment]
410+
transport.close()
411+
412+
397413
def test_sync_transport_wraps_unexpected_errors_with_sentinel_url_fallback():
398414
transport = SyncTransport(api_key="test-key")
399415
original_get = transport.client.get
@@ -490,6 +506,27 @@ async def failing_put(*args, **kwargs):
490506
asyncio.run(run())
491507

492508

509+
def test_async_transport_wraps_unexpected_errors_with_boolean_url_fallback():
510+
async def run() -> None:
511+
transport = AsyncTransport(api_key="test-key")
512+
original_put = transport.client.put
513+
514+
async def failing_put(*args, **kwargs):
515+
raise RuntimeError("boom")
516+
517+
transport.client.put = failing_put # type: ignore[assignment]
518+
try:
519+
with pytest.raises(
520+
HyperbrowserError, match="Request PUT unknown URL failed"
521+
):
522+
await transport.put(False) # type: ignore[arg-type]
523+
finally:
524+
transport.client.put = original_put # type: ignore[assignment]
525+
await transport.close()
526+
527+
asyncio.run(run())
528+
529+
493530
def test_async_transport_wraps_unexpected_errors_with_sentinel_url_fallback():
494531
async def run() -> None:
495532
transport = AsyncTransport(api_key="test-key")
@@ -600,6 +637,22 @@ def failing_get(*args, **kwargs):
600637
transport.close()
601638

602639

640+
def test_sync_transport_request_error_without_request_uses_unknown_url_for_boolean_input():
641+
transport = SyncTransport(api_key="test-key")
642+
original_get = transport.client.get
643+
644+
def failing_get(*args, **kwargs):
645+
raise httpx.RequestError("network down")
646+
647+
transport.client.get = failing_get # type: ignore[assignment]
648+
try:
649+
with pytest.raises(HyperbrowserError, match="Request GET unknown URL failed"):
650+
transport.get(False) # type: ignore[arg-type]
651+
finally:
652+
transport.client.get = original_get # type: ignore[assignment]
653+
transport.close()
654+
655+
603656
def test_sync_transport_request_error_without_request_uses_unknown_url_for_sentinel_input():
604657
transport = SyncTransport(api_key="test-key")
605658
original_get = transport.client.get
@@ -718,6 +771,27 @@ async def failing_delete(*args, **kwargs):
718771
asyncio.run(run())
719772

720773

774+
def test_async_transport_request_error_without_request_uses_unknown_url_for_boolean_input():
775+
async def run() -> None:
776+
transport = AsyncTransport(api_key="test-key")
777+
original_delete = transport.client.delete
778+
779+
async def failing_delete(*args, **kwargs):
780+
raise httpx.RequestError("network down")
781+
782+
transport.client.delete = failing_delete # type: ignore[assignment]
783+
try:
784+
with pytest.raises(
785+
HyperbrowserError, match="Request DELETE unknown URL failed"
786+
):
787+
await transport.delete(True) # type: ignore[arg-type]
788+
finally:
789+
transport.client.delete = original_delete # type: ignore[assignment]
790+
await transport.close()
791+
792+
asyncio.run(run())
793+
794+
721795
def test_async_transport_request_error_without_request_uses_unknown_url_for_sentinel_input():
722796
async def run() -> None:
723797
transport = AsyncTransport(api_key="test-key")

0 commit comments

Comments
 (0)