Introduction
Tenacity is a Python library that provides a clean decorator-based API for retrying operations that may fail transiently. It is the actively maintained fork of the retrying library, offering more features and better composability for production retry logic.
What Tenacity Does
- Retries any callable with a simple @retry decorator
- Supports exponential backoff, fixed delay, and random jitter wait strategies
- Lets you stop retrying after N attempts, a time limit, or a custom condition
- Filters which exceptions trigger a retry and which propagate immediately
- Provides callbacks for logging, metrics, or side effects on each retry
Architecture Overview
Tenacity wraps the decorated function in a retry controller that catches specified exceptions and re-invokes the function according to the configured wait and stop policies. Each retry attempt is tracked in a RetryCallState object that carries attempt count, elapsed time, and the last result or exception. The library is fully synchronous by default but also supports asyncio, Tornado, and trio coroutines.
Self-Hosting & Configuration
- Install from PyPI with pip install tenacity
- Apply the @retry decorator to any function or method
- Combine stop, wait, and retry conditions using the | and & operators
- Use before_sleep callbacks to log retry attempts
- Access retry statistics via the function's retry.statistics attribute
Key Features
- Composable stop, wait, and retry predicates with operator overloading
- Built-in support for asyncio, Tornado, and trio async frameworks
- Reraise option to surface the original exception after all retries fail
- Context manager form for retrying inline code blocks
- No external dependencies beyond the Python standard library
Comparison with Similar Tools
- urllib3 Retry — HTTP-specific retry built into urllib3, not general-purpose
- backoff — similar decorator approach but fewer composability options
- stamina — newer retry library focused on production defaults and observability
- retrying — the original library Tenacity forked from, now unmaintained
- Custom try/except loops — manual retry code that Tenacity replaces with a one-liner
FAQ
Q: Does Tenacity work with async functions? A: Yes. Use the same @retry decorator on async def functions. Tenacity detects coroutines automatically and awaits them correctly.
Q: Can I retry on specific return values instead of exceptions? A: Yes. Use retry_if_result with a predicate function to retry when the return value is unsatisfactory, such as an empty response.
Q: How do I log each retry attempt? A: Pass a before_sleep callback like before_sleep_log(logger, logging.WARNING) to emit a log line before each retry wait.
Q: Is it thread-safe? A: Yes. Each call gets its own RetryCallState, so concurrent threads retrying the same function do not interfere with each other.