reflexio
Composable, low-overhead retry policies with pluggable classification, per-class backoff strategies, and structured observability hooks.
Designed for services that need predictable retry behavior and clean integration with metrics/logging.
Documentation
- Site: https://aponysus.github.io/reflexio/
- Getting started: https://aponysus.github.io/reflexio/getting-started/
Installation
From PyPI:
uv pip install reflexio
# or
pip install reflexio
Quick Start
from reflexio.policy import RetryPolicy
from reflexio.classify import default_classifier
from reflexio.strategies import decorrelated_jitter
policy = RetryPolicy(
classifier=default_classifier,
strategy=decorrelated_jitter(max_s=10.0),
)
def flaky():
# your operation that may fail
...
result = policy.call(flaky)
Decorator quick start
from reflexio import retry, default_classifier
from reflexio.strategies import decorrelated_jitter
@retry # defaults to default_classifier + decorrelated_jitter(max_s=5.0)
def fetch_user():
...
# Or customize classifier/strategies
@retry(
classifier=default_classifier,
strategy=decorrelated_jitter(max_s=3.0),
)
def fetch_user_custom():
...
# Context manager for repeated calls with shared hooks/operation
policy = RetryPolicy(classifier=default_classifier, strategy=decorrelated_jitter(max_s=3.0))
with policy.context(operation="batch") as retry:
retry(fetch_user)
Async quick start
import asyncio
from reflexio import AsyncRetryPolicy, default_classifier
from reflexio.strategies import decorrelated_jitter
async_policy = AsyncRetryPolicy(
classifier=default_classifier,
strategy=decorrelated_jitter(max_s=5.0),
)
async def flaky_async():
...
asyncio.run(async_policy.call(flaky_async))
Why reflexio?
Most retry libraries give you either:
- decorators with a fixed backoff model, or
- one global strategy for all errors.
reflexio gives you something different:
✔ Exception → coarse error class mapping
Provided via default_classifier.
✔ Per-class strategy dispatch
Each ErrorClass can use its own backoff logic.
✔ Dependency-free strategies with jitter
decorrelated_jitter, equal_jitter, token_backoff.
✔ Deadlines, max attempts, and separate caps for UNKNOWN
Deterministic retry envelopes.
✔ Clean observability hook
Single callback for:
success, retry, permanent_fail, deadline_exceeded, max_attempts_exceeded, max_unknown_attempts_exceeded.
Error Classes & Classification
PERMANENT
CONCURRENCY
RATE_LIMIT
SERVER_ERROR
TRANSIENT
UNKNOWN
Classification rules:
- Explicit reflexio error types
- Numeric codes (
err.statusorerr.code) - Name heuristics
- Fallback to UNKNOWN
Metrics & Observability
def metric_hook(event, attempt, sleep_s, tags):
print(event, attempt, sleep_s, tags)
policy.call(my_op, on_metric=metric_hook)
Backoff Strategies
Strategy signature:
(attempt: int, klass: ErrorClass, prev_sleep: Optional[float]) -> float
Built‑ins:
decorrelated_jitter()equal_jitter()token_backoff()
Per-Class Example
policy = RetryPolicy(
classifier=default_classifier,
strategy=decorrelated_jitter(max_s=10.0), # default
strategies={
ErrorClass.CONCURRENCY: decorrelated_jitter(max_s=1.0),
ErrorClass.RATE_LIMIT: decorrelated_jitter(max_s=60.0),
ErrorClass.SERVER_ERROR: equal_jitter(max_s=30.0),
},
)
Deadline & Attempt Controls
policy = RetryPolicy(
classifier=default_classifier,
strategy=decorrelated_jitter(),
deadline_s=60,
max_attempts=8,
max_unknown_attempts=2,
)
Development
uv run pytest
Examples (in docs/snippets/)
- Sync httpx demo:
uv pip install httpxthenuv run python docs/snippets/httpx_sync_retry.py - Async httpx demo using
AsyncRetryPolicy:uv pip install httpxthenuv run python docs/snippets/httpx_async_retry.py - Async worker loop with retries:
uv run python docs/snippets/async_worker_retry.py - Decorator usage (sync + async):
uv run python docs/snippets/decorator_retry.py - FastAPI proxy with metrics counter:
uv pip install "fastapi[standard]" httpxthenuv run uvicorn docs.snippets.fastapi_downstream:app --reload - FastAPI middleware with per-endpoint policies:
uv pip install "fastapi[standard]" httpxthenuv run uvicorn docs.snippets.fastapi_middleware:app --reload - PyODBC + SQLSTATE classification example:
uv pip install pyodbcthenuv run python docs/snippets/pyodbc_retry.py - requests example:
uv pip install requeststhenuv run python docs/snippets/requests_retry.py - asyncpg example:
uv pip install asyncpgand setASYNC_PG_DSN, thenuv run python docs/snippets/asyncpg_retry.py - Pyperf microbenchmarks:
uv pip install .[dev]thenuv run python docs/snippets/bench_retry.py
Docs site
- Build/serve locally:
uv pip install .[docs]thenuv run mkdocs serve - Pages:
docs/index.md,docs/usage.md,docs/observability.md,docs/recipes.mdwith runnable snippets indocs/snippets/.
Versioning
Semantic Versioning.
Release files for reflexio 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| reflexio-0.2.2.tar.gz | 79.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| reflexio-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.9 kB
Release files / reflexio-0.2.2.tar.gz
| Download URL | reflexio-0.2.2.tar.gz |
|---|---|
| Size | 79.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f9dabac69c94e52afddbb6e71d176f1dec9d4ada64a09354ec5138f9f5898cef
|
|
BLAKE2b-256 checksum How to use checksums |
c0a6a683d1699862948ddb79773dea8fc3a7bc61a70eef4b525dcf1af1ce4dae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Release files / reflexio-0.2.2-py3-none-any.whl
| Download URL | reflexio-0.2.2-py3-none-any.whl |
|---|---|
| Size | 14.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
49418064927a1cb24cf9ae0c5d8c4a437a105e8d40e96298b5c7b17c28c7c9ec
|
|
BLAKE2b-256 checksum How to use checksums |
caf042052c16a2c372bd2d1e1537f6743973dc82e879f53ac120a3aea52b22b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|