DEVJHAWAR11
sync: deploy to HuggingFace Space
5b7955a
"""Retry decorator w/ exponential backoff."""
import time
import functools
from typing import Callable, ParamSpec, Tuple, Type, TypeVar
from .logger import get_logger
logger = get_logger(__name__)
P = ParamSpec("P")
R = TypeVar("R")
def with_retry(
max_retries: int = 3,
delay: float = 1.0,
backoff: float = 2.0,
exceptions: Tuple[Type[BaseException], ...] = (Exception,),
) -> Callable[[Callable[P, R]], Callable[P, R]]:
def decorator(func: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
current_delay = delay
last_exception: BaseException | None = None
for attempt in range(1, max_retries + 1):
try:
return func(*args, **kwargs) # type: ignore[arg-type]
except exceptions as exc:
last_exception = exc
if attempt == max_retries:
logger.error(
"Function '%s' failed after %d attempts: %s",
func.__name__,
max_retries,
exc,
)
raise
logger.warning(
"Function '%s' attempt %d/%d failed: %s — retrying in %.1fs",
func.__name__,
attempt,
max_retries,
exc,
current_delay,
)
time.sleep(current_delay)
current_delay *= backoff
raise last_exception # type: ignore[misc]
return wrapper # type: ignore[return-value]
return decorator