latest updates

This commit is contained in:
Matt Apperson
2025-12-04 10:46:19 -05:00
parent a1dae775cb
commit 6cd0d2a76d
84 changed files with 9465 additions and 8751 deletions
+69 -5
View File
@@ -3,7 +3,9 @@
import asyncio
import random
import time
from typing import List
from datetime import datetime
from email.utils import parsedate_to_datetime
from typing import List, Optional
import httpx
@@ -51,9 +53,11 @@ class Retries:
class TemporaryError(Exception):
response: httpx.Response
retry_after: Optional[int]
def __init__(self, response: httpx.Response):
self.response = response
self.retry_after = _parse_retry_after_header(response)
class PermanentError(Exception):
@@ -63,6 +67,62 @@ class PermanentError(Exception):
self.inner = inner
def _parse_retry_after_header(response: httpx.Response) -> Optional[int]:
"""Parse Retry-After header from response.
Returns:
Retry interval in milliseconds, or None if header is missing or invalid.
"""
retry_after_header = response.headers.get("retry-after")
if not retry_after_header:
return None
try:
seconds = float(retry_after_header)
return round(seconds * 1000)
except ValueError:
pass
try:
retry_date = parsedate_to_datetime(retry_after_header)
delta = (retry_date - datetime.now(retry_date.tzinfo)).total_seconds()
return round(max(0, delta) * 1000)
except (ValueError, TypeError):
pass
return None
def _get_sleep_interval(
exception: Exception,
initial_interval: int,
max_interval: int,
exponent: float,
retries: int,
) -> float:
"""Get sleep interval for retry with exponential backoff.
Args:
exception: The exception that triggered the retry.
initial_interval: Initial retry interval in milliseconds.
max_interval: Maximum retry interval in milliseconds.
exponent: Base for exponential backoff calculation.
retries: Current retry attempt count.
Returns:
Sleep interval in seconds.
"""
if (
isinstance(exception, TemporaryError)
and exception.retry_after is not None
and exception.retry_after > 0
):
return exception.retry_after / 1000
sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
return min(sleep, max_interval / 1000)
def retry(func, retries: Retries):
if retries.config.strategy == "backoff":
@@ -183,8 +243,10 @@ def retry_with_backoff(
return exception.response
raise
sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
sleep = min(sleep, max_interval / 1000)
sleep = _get_sleep_interval(
exception, initial_interval, max_interval, exponent, retries
)
time.sleep(sleep)
retries += 1
@@ -211,7 +273,9 @@ async def retry_with_backoff_async(
return exception.response
raise
sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1)
sleep = min(sleep, max_interval / 1000)
sleep = _get_sleep_interval(
exception, initial_interval, max_interval, exponent, retries
)
await asyncio.sleep(sleep)
retries += 1