Retry Decorator With Backoff

Decorate a function so transient exceptions are retried a fixed number of times with growing delay, keeping call sites free of retry plumbing.

PythonResilience

Code

snippet
import functools
import time
from typing import Callable, Tuple, Type, TypeVar

T = TypeVar("T")


def retry(
    attempts: int = 3,
    delay: float = 0.1,
    backoff: float = 2.0,
    exceptions: Tuple[Type[BaseException], ...] = (Exception,),
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @functools.wraps(func)
        def wrapper(*args, **kwargs) -> T:
            remaining = attempts
            wait = delay
            while True:
                try:
                    return func(*args, **kwargs)
                except exceptions:
                    remaining -= 1
                    if remaining <= 0:
                        raise
                    time.sleep(wait)
                    wait *= backoff

        return wrapper

    return decorator