Skip to content

Commit d5f6fb9

Browse files
Add memoryview status metadata retry regression coverage
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent d2f50a2 commit d5f6fb9

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

hyperbrowser/client/polling.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,13 @@ def _normalize_status_code_for_retry(status_code: object) -> Optional[int]:
180180
if isinstance(status_code, int):
181181
return status_code
182182
status_text: Optional[str] = None
183-
if isinstance(status_code, (bytes, bytearray)):
183+
if isinstance(status_code, memoryview):
184+
status_bytes = status_code.tobytes()
185+
try:
186+
status_text = status_bytes.decode("ascii")
187+
except UnicodeDecodeError:
188+
return None
189+
elif isinstance(status_code, (bytes, bytearray)):
184190
try:
185191
status_text = bytes(status_code).decode("ascii")
186192
except UnicodeDecodeError:

tests/test_polling.py

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

147147

148+
def test_poll_until_terminal_status_does_not_retry_memoryview_client_errors():
149+
attempts = {"count": 0}
150+
151+
def get_status() -> str:
152+
attempts["count"] += 1
153+
raise HyperbrowserError(
154+
"client failure",
155+
status_code=memoryview(b"400"), # type: ignore[arg-type]
156+
)
157+
158+
with pytest.raises(HyperbrowserError, match="client failure"):
159+
poll_until_terminal_status(
160+
operation_name="sync poll memoryview client error",
161+
get_status=get_status,
162+
is_terminal_status=lambda value: value == "completed",
163+
poll_interval_seconds=0.0001,
164+
max_wait_seconds=1.0,
165+
max_status_failures=5,
166+
)
167+
168+
assert attempts["count"] == 1
169+
170+
148171
def test_poll_until_terminal_status_retries_overlong_numeric_status_codes():
149172
attempts = {"count": 0}
150173

@@ -732,6 +755,29 @@ def operation() -> str:
732755
assert attempts["count"] == 3
733756

734757

758+
def test_retry_operation_retries_memoryview_rate_limit_errors():
759+
attempts = {"count": 0}
760+
761+
def operation() -> str:
762+
attempts["count"] += 1
763+
if attempts["count"] < 3:
764+
raise HyperbrowserError(
765+
"rate limited",
766+
status_code=memoryview(b"429"), # type: ignore[arg-type]
767+
)
768+
return "ok"
769+
770+
result = retry_operation(
771+
operation_name="sync retry memoryview rate limit",
772+
operation=operation,
773+
max_attempts=5,
774+
retry_delay_seconds=0.0001,
775+
)
776+
777+
assert result == "ok"
778+
assert attempts["count"] == 3
779+
780+
735781
def test_retry_operation_does_not_retry_numeric_bytes_client_errors():
736782
attempts = {"count": 0}
737783

@@ -1189,6 +1235,32 @@ async def get_status() -> str:
11891235
asyncio.run(run())
11901236

11911237

1238+
def test_poll_until_terminal_status_async_does_not_retry_memoryview_client_errors():
1239+
async def run() -> None:
1240+
attempts = {"count": 0}
1241+
1242+
async def get_status() -> str:
1243+
attempts["count"] += 1
1244+
raise HyperbrowserError(
1245+
"client failure",
1246+
status_code=memoryview(b"404"), # type: ignore[arg-type]
1247+
)
1248+
1249+
with pytest.raises(HyperbrowserError, match="client failure"):
1250+
await poll_until_terminal_status_async(
1251+
operation_name="async poll memoryview client error",
1252+
get_status=get_status,
1253+
is_terminal_status=lambda value: value == "completed",
1254+
poll_interval_seconds=0.0001,
1255+
max_wait_seconds=1.0,
1256+
max_status_failures=5,
1257+
)
1258+
1259+
assert attempts["count"] == 1
1260+
1261+
asyncio.run(run())
1262+
1263+
11921264
def test_poll_until_terminal_status_async_retries_overlong_numeric_status_codes():
11931265
async def run() -> None:
11941266
attempts = {"count": 0}
@@ -1470,6 +1542,32 @@ async def operation() -> str:
14701542
asyncio.run(run())
14711543

14721544

1545+
def test_retry_operation_async_retries_memoryview_rate_limit_errors():
1546+
async def run() -> None:
1547+
attempts = {"count": 0}
1548+
1549+
async def operation() -> str:
1550+
attempts["count"] += 1
1551+
if attempts["count"] < 3:
1552+
raise HyperbrowserError(
1553+
"rate limited",
1554+
status_code=memoryview(b"429"), # type: ignore[arg-type]
1555+
)
1556+
return "ok"
1557+
1558+
result = await retry_operation_async(
1559+
operation_name="async retry memoryview rate limit",
1560+
operation=operation,
1561+
max_attempts=5,
1562+
retry_delay_seconds=0.0001,
1563+
)
1564+
1565+
assert result == "ok"
1566+
assert attempts["count"] == 3
1567+
1568+
asyncio.run(run())
1569+
1570+
14731571
def test_retry_operation_async_retries_numeric_bytes_rate_limit_errors():
14741572
async def run() -> None:
14751573
attempts = {"count": 0}

0 commit comments

Comments
 (0)