Skip to content

Commit edc0c06

Browse files
Broaden async loop mismatch runtime error detection
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent b847fff commit edc0c06

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

hyperbrowser/client/polling.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ def _is_async_loop_contract_runtime_error(exc: Exception) -> bool:
134134
normalized_message = str(exc).lower()
135135
if "event loop is closed" in normalized_message:
136136
return True
137-
return "different loop" in normalized_message and "future" in normalized_message
137+
if "different event loop" in normalized_message:
138+
return True
139+
return "different loop" in normalized_message and any(
140+
marker in normalized_message for marker in ("future", "task")
141+
)
138142

139143

140144
def _is_retryable_exception(exc: Exception) -> bool:

tests/test_polling.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,24 @@ def operation() -> str:
12021202
assert attempts["count"] == 1
12031203

12041204

1205+
def test_retry_operation_does_not_retry_runtime_errors_for_event_loop_binding():
1206+
attempts = {"count": 0}
1207+
1208+
def operation() -> str:
1209+
attempts["count"] += 1
1210+
raise RuntimeError("Task is bound to a different event loop")
1211+
1212+
with pytest.raises(RuntimeError, match="different event loop"):
1213+
retry_operation(
1214+
operation_name="sync retry loop-binding runtime error",
1215+
operation=operation,
1216+
max_attempts=5,
1217+
retry_delay_seconds=0.0001,
1218+
)
1219+
1220+
assert attempts["count"] == 1
1221+
1222+
12051223
def test_poll_until_terminal_status_async_does_not_retry_runtime_errors_for_closed_loop():
12061224
async def run() -> None:
12071225
attempts = {"count": 0}
@@ -1225,6 +1243,29 @@ async def get_status() -> str:
12251243
asyncio.run(run())
12261244

12271245

1246+
def test_poll_until_terminal_status_async_does_not_retry_runtime_errors_for_event_loop_binding():
1247+
async def run() -> None:
1248+
attempts = {"count": 0}
1249+
1250+
async def get_status() -> str:
1251+
attempts["count"] += 1
1252+
raise RuntimeError("Task is bound to a different event loop")
1253+
1254+
with pytest.raises(RuntimeError, match="different event loop"):
1255+
await poll_until_terminal_status_async(
1256+
operation_name="async poll loop-binding runtime error",
1257+
get_status=get_status,
1258+
is_terminal_status=lambda value: value == "completed",
1259+
poll_interval_seconds=0.0001,
1260+
max_wait_seconds=1.0,
1261+
max_status_failures=5,
1262+
)
1263+
1264+
assert attempts["count"] == 1
1265+
1266+
asyncio.run(run())
1267+
1268+
12281269
def test_async_poll_until_terminal_status_allows_immediate_terminal_on_zero_max_wait():
12291270
async def run() -> None:
12301271
status = await poll_until_terminal_status_async(

0 commit comments

Comments
 (0)