Create a retry decorator with max attempts in Python

A decorator that retries a function up to a specified number of times when it raises an exception, with an optional delay between attempts.

Easy Python 3.9+ Aug 9, 2026 Functions & basics 12 views 0 copies

Python code

31 lines
Python 3.9+
import functools
import time


def retry(max_attempts, delay=0.1):
    """Retry a function up to max_attempts times on exception."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts:
                        raise
                    print(f"Attempt {attempt} failed: {e}. Retrying...")
                    time.sleep(delay)
        return wrapper
    return decorator


@retry(max_attempts=3, delay=0.05)
def flaky_operation():
    import random
    if random.random() < 0.6:
        raise ValueError("Transient failure")
    return "Success"


if __name__ == "__main__":
    print(flaky_operation())

Output

stdout
Attempt 1 failed: Transient failure. Retrying...
Success

How it works

The retry decorator wraps a function so that each call enters a loop up to max_attempts times. On each exception, it checks if it's the last attempt and either raises the original exception or prints a message and sleeps. functools.wraps preserves the original function's metadata. This pattern is useful for handling transient failures in network calls or external services.

Common mistakes

  • Catching `Exception` may hide programmer errors like `KeyboardInterrupt` or `SystemExit`.
  • Forgetting to re-raise on the final attempt, making the function return `None` silently.
  • Using a fixed delay without backoff, causing repeated immediate retries.

Variations

  1. Add exponential backoff with `time.sleep(delay * (2 ** (attempt - 1)))`.
  2. Use `retry` from third-party libraries like `tenacity` for more control.

Real-world use cases

  • Retrying a network request to an API that occasionally fails with a 503 status.
  • Handling transient database connection errors when running a batch job.
  • Retrying a file upload to cloud storage when the connection drops mid-transfer.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Functions & basics

Related tutorials and quizzes for this topic.