Skip to content

Commit 47d3701

Browse files
Reject integer subclasses in polling numeric validators
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent c8ec996 commit 47d3701

2 files changed

Lines changed: 78 additions & 8 deletions

File tree

hyperbrowser/client/polling.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,17 +458,15 @@ def _validate_retry_config(
458458
retry_delay_seconds: float,
459459
max_status_failures: Optional[int] = None,
460460
) -> float:
461-
if isinstance(max_attempts, bool) or not isinstance(max_attempts, int):
461+
if type(max_attempts) is not int:
462462
raise HyperbrowserError("max_attempts must be an integer")
463463
if max_attempts < 1:
464464
raise HyperbrowserError("max_attempts must be at least 1")
465465
normalized_retry_delay_seconds = _normalize_non_negative_real(
466466
retry_delay_seconds, field_name="retry_delay_seconds"
467467
)
468468
if max_status_failures is not None:
469-
if isinstance(max_status_failures, bool) or not isinstance(
470-
max_status_failures, int
471-
):
469+
if type(max_status_failures) is not int:
472470
raise HyperbrowserError("max_status_failures must be an integer")
473471
if max_status_failures < 1:
474472
raise HyperbrowserError("max_status_failures must be at least 1")
@@ -494,11 +492,11 @@ def _validate_page_batch_values(
494492
current_page_batch: int,
495493
total_page_batches: int,
496494
) -> None:
497-
if isinstance(current_page_batch, bool) or not isinstance(current_page_batch, int):
495+
if type(current_page_batch) is not int:
498496
raise HyperbrowserPollingError(
499497
f"Invalid current page batch for {operation_name}: expected integer"
500498
)
501-
if isinstance(total_page_batches, bool) or not isinstance(total_page_batches, int):
499+
if type(total_page_batches) is not int:
502500
raise HyperbrowserPollingError(
503501
f"Invalid total page batches for {operation_name}: expected integer"
504502
)

tests/test_polling.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ def _raise_isfinite_error(value: float) -> bool:
6868
assert exc_info.value.original_error is not None
6969

7070

71-
def test_poll_until_terminal_status_wraps_unexpected_float_conversion_failures(
72-
):
71+
def test_poll_until_terminal_status_wraps_unexpected_float_conversion_failures():
7372
class _BrokenDecimal(Decimal):
7473
def __float__(self) -> float:
7574
raise RuntimeError("unexpected float conversion failure")
@@ -4080,6 +4079,30 @@ def test_collect_paginated_results_raises_on_invalid_page_batch_types():
40804079
)
40814080

40824081

4082+
def test_collect_paginated_results_raises_on_integer_subclass_page_batch_types():
4083+
class _IntSubclass(int):
4084+
pass
4085+
4086+
with pytest.raises(
4087+
HyperbrowserPollingError,
4088+
match="Invalid total page batches for sync paginated int-subclass types",
4089+
):
4090+
collect_paginated_results(
4091+
operation_name="sync paginated int-subclass types",
4092+
get_next_page=lambda page: {
4093+
"current": 1,
4094+
"total": _IntSubclass(2),
4095+
"items": [],
4096+
},
4097+
get_current_page_batch=lambda response: response["current"],
4098+
get_total_page_batches=lambda response: response["total"],
4099+
on_page_success=lambda response: None,
4100+
max_wait_seconds=1.0,
4101+
max_attempts=2,
4102+
retry_delay_seconds=0.0001,
4103+
)
4104+
4105+
40834106
def test_collect_paginated_results_raises_on_boolean_page_batch_values():
40844107
with pytest.raises(
40854108
HyperbrowserPollingError,
@@ -4179,6 +4202,32 @@ async def run() -> None:
41794202
asyncio.run(run())
41804203

41814204

4205+
def test_collect_paginated_results_async_raises_on_integer_subclass_page_batch_types():
4206+
class _IntSubclass(int):
4207+
pass
4208+
4209+
async def run() -> None:
4210+
with pytest.raises(
4211+
HyperbrowserPollingError,
4212+
match="Invalid current page batch for async paginated int-subclass types",
4213+
):
4214+
await collect_paginated_results_async(
4215+
operation_name="async paginated int-subclass types",
4216+
get_next_page=lambda page: asyncio.sleep(
4217+
0,
4218+
result={"current": _IntSubclass(1), "total": 2, "items": []},
4219+
),
4220+
get_current_page_batch=lambda response: response["current"],
4221+
get_total_page_batches=lambda response: response["total"],
4222+
on_page_success=lambda response: None,
4223+
max_wait_seconds=1.0,
4224+
max_attempts=2,
4225+
retry_delay_seconds=0.0001,
4226+
)
4227+
4228+
asyncio.run(run())
4229+
4230+
41824231
def test_collect_paginated_results_async_raises_on_boolean_page_batch_values():
41834232
async def run() -> None:
41844233
with pytest.raises(
@@ -6686,6 +6735,9 @@ async def run() -> None:
66866735

66876736

66886737
def test_polling_helpers_validate_retry_and_interval_configuration():
6738+
class _IntSubclass(int):
6739+
pass
6740+
66896741
with pytest.raises(HyperbrowserError, match="max_attempts must be at least 1"):
66906742
retry_operation(
66916743
operation_name="invalid-retry",
@@ -6749,6 +6801,14 @@ def test_polling_helpers_validate_retry_and_interval_configuration():
67496801
retry_delay_seconds=0,
67506802
)
67516803

6804+
with pytest.raises(HyperbrowserError, match="max_attempts must be an integer"):
6805+
retry_operation(
6806+
operation_name="invalid-retry-int-subclass",
6807+
operation=lambda: "ok",
6808+
max_attempts=_IntSubclass(1), # type: ignore[arg-type]
6809+
retry_delay_seconds=0,
6810+
)
6811+
67526812
with pytest.raises(
67536813
HyperbrowserError, match="retry_delay_seconds must be non-negative"
67546814
):
@@ -6783,6 +6843,18 @@ def test_polling_helpers_validate_retry_and_interval_configuration():
67836843
max_status_failures=1.5, # type: ignore[arg-type]
67846844
)
67856845

6846+
with pytest.raises(
6847+
HyperbrowserError, match="max_status_failures must be an integer"
6848+
):
6849+
poll_until_terminal_status(
6850+
operation_name="invalid-status-failures-int-subclass",
6851+
get_status=lambda: "completed",
6852+
is_terminal_status=lambda value: value == "completed",
6853+
poll_interval_seconds=0.1,
6854+
max_wait_seconds=1.0,
6855+
max_status_failures=_IntSubclass(1), # type: ignore[arg-type]
6856+
)
6857+
67866858
with pytest.raises(
67876859
HyperbrowserError, match="poll_interval_seconds must be non-negative"
67886860
):

0 commit comments

Comments
 (0)