retry
A lightweight, dependency-free Python decorator for retrying flaky function calls with configurable delay, backoff, and jitter.
Repository Health
Technical Analysis
retry is a small, dependency-free Python library that turns any function into one that automatically retries when it raises an exception. A single decorator (@retry) or a direct call (retry_call) wraps your function with a configurable retry count, delay, exponential backoff, jitter, and a maximum delay cap, so transient failures like network hiccups, rate limits, or flaky I/O are absorbed without hand-rolled retry loops scattered across a codebase.
Under the hood both entry points funnel into one internal retry loop, keeping behavior consistent whether you use the decorator syntax for static call sites or retry_call for cases where retry parameters need to be chosen dynamically at runtime. It ships with zero required dependencies — an optional decorator package can be installed to preserve wrapped functions’ original signatures, but the library works fine without it.
What You Get
- A
@retrydecorator that wraps any function with configurable retry behavior - A
retry_callfunction for retrying a callable with arguments chosen at runtime - Exponential backoff with an optional maximum delay cap
- Fixed or randomized jitter added to the delay between attempts
- Per-call exception filtering (retry on one exception type, a tuple of types, or all exceptions)
- Optional logging of each failed attempt via a configurable logger
Common Use Cases
- Retrying flaky network calls to third-party APIs that occasionally time out or rate-limit
- Polling a resource until it becomes available, with capped exponential backoff
- Wrapping database or queue operations that can fail transiently under contention
- Adding resilience to scripts and CLI tools without pulling in a heavier retry/resilience framework
Under The Hood
Architecture
The library is a single flat Python package (retry/) with three modules: api.py holds the actual retry logic, compat.py isolates Python 2/3 and optional-dependency compatibility shims, and __init__.py re-exports the two public entry points. Both retry (a decorator) and retry_call (a direct call) funnel into one private helper, __retry_internal, which owns the entire retry loop — attempt counting, exception filtering, delay computation, exponential backoff, jitter, and the max-delay cap — so the two call styles never diverge in behavior. The decorator itself is implemented via a decorator-module-or-fallback wrapper in compat.py: if the optional decorator package is installed it is used to preserve the wrapped function’s signature; otherwise a functools.wraps-based fallback is used instead, at the cost of signature transparency. There is no internal state beyond the closures created per call, no classes, and no external I/O beyond time.sleep and logging — a deliberately minimal design that would break only if the shared internal loop’s signature changed, since every public entry point depends on it directly.
Tech Stack
retry targets plain CPython/PyPy with no required third-party dependencies, relying solely on logging, random, time, and functools from the standard library; the one optional runtime dependency is the decorator package (pinned loosely at >=3.4.2 in requirements.txt), used only to preserve function signatures through the decorator. Packaging is handled by setuptools combined with pbr (OpenStack’s Python Build Reasonableness tool), which derives the package version and changelog metadata from AUTHORS/ChangeLog rather than a hardcoded version string. Testing uses pytest with mock/unittest.mock, orchestrated across multiple Python versions (2.6, 2.7, 3.4, 3.5, PyPy) via tox, with a separate flake8 tox environment for linting; there is no database, web framework, or deployment target since this is a pure library.
Code Quality
Test coverage lives in a single tests/test_retry.py file with seven pytest tests that exercise the core behaviors directly: exhausting retries and re-raising, infinite (-1 and float('inf')) try counts, max_delay capping, fixed jitter accumulation, and both positional- and keyword-argument variants of retry_call, using monkeypatch to fake time.sleep so tests run instantly and deterministically. Error handling is intentionally simple — the code re-raises the original exception once tries are exhausted rather than wrapping it, preserving the original traceback as the README advertises. There are no type hints anywhere (the project still targets Python 2.6+), and while a flake8 lint environment is configured in tox.ini, no CI workflow (e.g. GitHub Actions) is present in the repository, so linting and the tox matrix appear to run only locally or via an external, undocumented system.
API Design
retry does not introduce a novel retry algorithm — exponential backoff, jitter, and exception-filtered retries are standard patterns also found in libraries like tenacity and backoff. Its distinguishing choice is minimalism: a single, short module with zero required dependencies and two small, orthogonal entry points (a decorator for static call sites, a direct-call function for dynamic parameters) rather than a configurable strategy/policy object model. That makes the whole public surface easy to read in full and reason about, but it also means it lacks the richer strategy composition, async support, and callback hooks that more actively maintained retry libraries provide.
Used by 3 apps in this directory
auto-news
AI Assistants · Productivity
An AI-powered personal news aggregator that filters multi-source feeds through LLMs and delivers curated, noise-free summaries to your Notion workspace.
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.
Qdrant
Databases · AI Development · Search
Open-source vector database and search engine built in Rust for production-grade AI applications — from semantic search to RAG pipelines and recommendation systems.