Getting Started
This guide will walk you through the basics of using backoff for retrying functions.
Installation
Install backoff using pip:
pip install python-backoff
Basic Concepts
Backoff provides two main decorators:
@backoff.on_exception- Retry when a specific exception is raised@backoff.on_predicate- Retry when a condition is true about the return value
Your First Retry
Let's start with a simple example - retrying a network request:
import backoff
import requests
@backoff.on_exception(backoff.expo,
requests.exceptions.RequestException)
def get_url(url):
return requests.get(url)
This decorator will:
- Retry whenever
RequestException(or any subclass) is raised - Use exponential backoff (wait times: 1s, 2s, 4s, 8s, 16s, ...)
- Keep retrying indefinitely until success
Adding Limits
In production, you'll want to limit retries:
@backoff.on_exception(backoff.expo,
requests.exceptions.RequestException,
max_time=60,
max_tries=5)
def get_url(url):
return requests.get(url)
This will give up after either:
- 60 seconds have elapsed, OR
- 5 retry attempts have been made
Handling Multiple Exceptions
You can retry on multiple exception types:
@backoff.on_exception(
backoff.expo,
(requests.exceptions.Timeout,
requests.exceptions.ConnectionError),
max_time=30
)
def get_url(url):
return requests.get(url)
Conditional Give-Up
Sometimes you need custom logic to decide when to stop retrying:
def fatal_code(e):
"""Don't retry on 4xx errors"""
return 400 <= e.response.status_code < 500
@backoff.on_exception(
backoff.expo,
requests.exceptions.RequestException,
max_time=300,
giveup=fatal_code
)
def get_url(url):
return requests.get(url)
Using on_predicate
For polling or checking return values:
@backoff.on_predicate(backoff.constant,
lambda result: result is None,
interval=5,
max_time=300)
def check_job_status(job_id):
response = requests.get(f"/jobs/{job_id}")
if response.json()["status"] == "complete":
return response.json()
return None # Will trigger retry
Wait Strategies
Backoff provides several wait strategies:
Exponential (expo)
@backoff.on_exception(backoff.expo, Exception)
Wait times: 1s, 2s, 4s, 8s, 16s, ...
Fibonacci (fibo)
@backoff.on_exception(backoff.fibo, Exception)
Wait times: 1s, 1s, 2s, 3s, 5s, 8s, 13s, ...
Constant
@backoff.on_exception(backoff.constant, Exception, interval=5)
Wait times: 5s, 5s, 5s, 5s, ...
Event Handlers
Track what's happening during retries:
def log_backoff(details):
print(f"Backing off {details['wait']:.1f} seconds after {details['tries']} tries")
def log_success(details):
print(f"Success after {details['tries']} tries")
@backoff.on_exception(
backoff.expo,
requests.exceptions.RequestException,
on_backoff=log_backoff,
on_success=log_success,
max_tries=5
)
def get_url(url):
return requests.get(url)
Async Support
Backoff works seamlessly with async functions:
import aiohttp
@backoff.on_exception(backoff.expo,
aiohttp.ClientError,
max_time=60)
async def get_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
Next Steps
- Decorators Guide - Deep dive into decorators
- Wait Strategies - All available strategies
- Configuration - Advanced configuration options
- Examples - Real-world examples