Skip to content

Commit af53b2a

Browse files
Add specialized timeout and polling exception types
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent f6be5ad commit af53b2a

3 files changed

Lines changed: 45 additions & 8 deletions

File tree

hyperbrowser/client/polling.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import time
33
from typing import Awaitable, Callable, Optional, TypeVar
44

5-
from hyperbrowser.exceptions import HyperbrowserError
5+
from hyperbrowser.exceptions import (
6+
HyperbrowserError,
7+
HyperbrowserPollingError,
8+
HyperbrowserTimeoutError,
9+
)
610

711
T = TypeVar("T")
812

@@ -27,7 +31,7 @@ def poll_until_terminal_status(
2731

2832
while True:
2933
if has_exceeded_max_wait(start_time, max_wait_seconds):
30-
raise HyperbrowserError(
34+
raise HyperbrowserTimeoutError(
3135
f"Timed out waiting for {operation_name} after {max_wait_seconds} seconds"
3236
)
3337

@@ -37,7 +41,7 @@ def poll_until_terminal_status(
3741
except Exception as exc:
3842
failures += 1
3943
if failures >= max_status_failures:
40-
raise HyperbrowserError(
44+
raise HyperbrowserPollingError(
4145
f"Failed to poll {operation_name} after {max_status_failures} attempts: {exc}"
4246
) from exc
4347
time.sleep(poll_interval_seconds)
@@ -82,7 +86,7 @@ async def poll_until_terminal_status_async(
8286

8387
while True:
8488
if has_exceeded_max_wait(start_time, max_wait_seconds):
85-
raise HyperbrowserError(
89+
raise HyperbrowserTimeoutError(
8690
f"Timed out waiting for {operation_name} after {max_wait_seconds} seconds"
8791
)
8892

@@ -92,7 +96,7 @@ async def poll_until_terminal_status_async(
9296
except Exception as exc:
9397
failures += 1
9498
if failures >= max_status_failures:
95-
raise HyperbrowserError(
99+
raise HyperbrowserPollingError(
96100
f"Failed to poll {operation_name} after {max_status_failures} attempts: {exc}"
97101
) from exc
98102
await asyncio.sleep(poll_interval_seconds)

hyperbrowser/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,11 @@ def __str__(self) -> str:
3636

3737
def __repr__(self) -> str:
3838
return self.__str__()
39+
40+
41+
class HyperbrowserTimeoutError(HyperbrowserError):
42+
"""Raised when a polling or wait operation exceeds configured timeout."""
43+
44+
45+
class HyperbrowserPollingError(HyperbrowserError):
46+
"""Raised when a polling operation repeatedly fails to retrieve status."""

tests/test_polling.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
retry_operation,
99
retry_operation_async,
1010
)
11-
from hyperbrowser.exceptions import HyperbrowserError
11+
from hyperbrowser.exceptions import (
12+
HyperbrowserError,
13+
HyperbrowserPollingError,
14+
HyperbrowserTimeoutError,
15+
)
1216

1317

1418
def test_poll_until_terminal_status_returns_terminal_value():
@@ -26,7 +30,9 @@ def test_poll_until_terminal_status_returns_terminal_value():
2630

2731

2832
def test_poll_until_terminal_status_times_out():
29-
with pytest.raises(HyperbrowserError, match="Timed out waiting for sync timeout"):
33+
with pytest.raises(
34+
HyperbrowserTimeoutError, match="Timed out waiting for sync timeout"
35+
):
3036
poll_until_terminal_status(
3137
operation_name="sync timeout",
3238
get_status=lambda: "running",
@@ -57,7 +63,9 @@ def get_status() -> str:
5763

5864

5965
def test_poll_until_terminal_status_raises_after_status_failures():
60-
with pytest.raises(HyperbrowserError, match="Failed to poll sync poll failure"):
66+
with pytest.raises(
67+
HyperbrowserPollingError, match="Failed to poll sync poll failure"
68+
):
6169
poll_until_terminal_status(
6270
operation_name="sync poll failure",
6371
get_status=lambda: (_ for _ in ()).throw(ValueError("always")),
@@ -149,3 +157,20 @@ async def get_status() -> str:
149157
assert status == "completed"
150158

151159
asyncio.run(run())
160+
161+
162+
def test_async_poll_until_terminal_status_raises_after_status_failures():
163+
async def run() -> None:
164+
with pytest.raises(
165+
HyperbrowserPollingError, match="Failed to poll async poll failure"
166+
):
167+
await poll_until_terminal_status_async(
168+
operation_name="async poll failure",
169+
get_status=lambda: (_ for _ in ()).throw(ValueError("always")),
170+
is_terminal_status=lambda value: value in {"completed", "failed"},
171+
poll_interval_seconds=0.0001,
172+
max_wait_seconds=1.0,
173+
max_status_failures=2,
174+
)
175+
176+
asyncio.run(run())

0 commit comments

Comments
 (0)