retry.pyโข2.8 kB
"""Retry logic with exponential backoff."""
import asyncio
import logging
from typing import TypeVar, Callable, Any
from functools import wraps
logger = logging.getLogger(__name__)
T = TypeVar("T")
async def retry_with_backoff(
func: Callable[..., Any],
*args: Any,
max_retries: int = 3,
initial_delay: float = 1.0,
backoff_factor: float = 2.0,
exceptions: tuple = (Exception,),
**kwargs: Any,
) -> Any:
"""
Retry an async function with exponential backoff.
Args:
func: Async function to retry
*args: Positional arguments for func
max_retries: Maximum number of retry attempts
initial_delay: Initial delay in seconds
backoff_factor: Multiplier for delay on each retry
exceptions: Tuple of exceptions to catch and retry
**kwargs: Keyword arguments for func
Returns:
Result of successful function call
Raises:
Last exception if all retries fail
"""
delay = initial_delay
last_exception = None
for attempt in range(max_retries + 1):
try:
return await func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt == max_retries:
logger.error(
f"Failed after {max_retries} retries: {str(e)}"
)
raise
logger.warning(
f"Attempt {attempt + 1}/{max_retries + 1} failed: {str(e)}. "
f"Retrying in {delay:.1f}s..."
)
await asyncio.sleep(delay)
delay *= backoff_factor
# This should never be reached, but just in case
if last_exception:
raise last_exception
def with_retry(
max_retries: int = 3,
initial_delay: float = 1.0,
backoff_factor: float = 2.0,
exceptions: tuple = (Exception,),
):
"""
Decorator for adding retry logic to async functions.
Args:
max_retries: Maximum number of retry attempts
initial_delay: Initial delay in seconds
backoff_factor: Multiplier for delay on each retry
exceptions: Tuple of exceptions to catch and retry
Example:
@with_retry(max_retries=3, initial_delay=1.0)
async def fetch_data():
return await api.get('/data')
"""
def decorator(func: Callable) -> Callable:
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
return await retry_with_backoff(
func,
*args,
max_retries=max_retries,
initial_delay=initial_delay,
backoff_factor=backoff_factor,
exceptions=exceptions,
**kwargs,
)
return wrapper
return decorator