Spaces:
Running
Running
| """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: | |
| 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 | |