Skip to content
GreenSnippets

Retry decorator with exponential backoff

Re-run a flaky function a limited number of times with growing delays, only for chosen exception types.

Python
import functools
import time


def retry(attempts=3, delay=0.5, backoff=2.0, exceptions=(Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            wait = delay
            for attempt in range(1, attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions:
                    if attempt == attempts:
                        raise
                    time.sleep(wait)
                    wait *= backoff
        return wrapper
    return decorator

How it works

A decorator keeps retry logic out of business code. Only the exceptions you list are retried; anything else propagates immediately. The last exception is re-raised once attempts run out, and functools.wraps preserves the wrapped function's name and docstring.

Retry only idempotent operations, and add jitter if many processes may retry together.