Skip to content

Commit 301e206

Browse files
Cancel returned futures for invalid non-awaitable callbacks
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent f461957 commit 301e206

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

hyperbrowser/client/polling.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def _ensure_non_awaitable(
9292
if inspect.isawaitable(result):
9393
if inspect.iscoroutine(result):
9494
result.close()
95+
elif isinstance(result, asyncio.Future) and not result.done():
96+
result.cancel()
9597
raise _NonRetryablePollingError(
9698
f"{callback_name} must return a non-awaitable result for {operation_name}"
9799
)

tests/test_polling.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,29 @@ def get_status() -> str:
363363
assert attempts["count"] == 1
364364

365365

366+
def test_poll_until_terminal_status_cancels_future_terminal_callback_results():
367+
loop = asyncio.new_event_loop()
368+
try:
369+
callback_future = loop.create_future()
370+
371+
with pytest.raises(
372+
HyperbrowserError,
373+
match="is_terminal_status must return a non-awaitable result",
374+
):
375+
poll_until_terminal_status(
376+
operation_name="sync terminal callback future",
377+
get_status=lambda: "completed",
378+
is_terminal_status=lambda value: callback_future, # type: ignore[return-value]
379+
poll_interval_seconds=0.0001,
380+
max_wait_seconds=1.0,
381+
max_status_failures=5,
382+
)
383+
384+
assert callback_future.cancelled()
385+
finally:
386+
loop.close()
387+
388+
366389
def test_retry_operation_retries_and_returns_value():
367390
attempts = {"count": 0}
368391

@@ -550,6 +573,26 @@ def operation() -> object:
550573
assert attempts["count"] == 1
551574

552575

576+
def test_retry_operation_cancels_future_operation_results():
577+
loop = asyncio.new_event_loop()
578+
try:
579+
operation_future = loop.create_future()
580+
581+
with pytest.raises(
582+
HyperbrowserError, match="operation must return a non-awaitable result"
583+
):
584+
retry_operation(
585+
operation_name="sync retry future callback",
586+
operation=lambda: operation_future, # type: ignore[return-value]
587+
max_attempts=5,
588+
retry_delay_seconds=0.0001,
589+
)
590+
591+
assert operation_future.cancelled()
592+
finally:
593+
loop.close()
594+
595+
553596
def test_async_polling_and_retry_helpers():
554597
async def run() -> None:
555598
status_values = iter(["pending", "completed"])

0 commit comments

Comments
 (0)