Unified resilience patterns for Python — retry, circuit breaker, timeout, fallback, and bulkhead in one decorator.
Project description
pyresilience
Unified resilience patterns for Python — retry, circuit breaker, timeout, fallback, bulkhead, rate limiter, and cache in one decorator. Python's Resilience4j.
Why?
Python has tenacity for retry, pybreaker for circuit breaking, and wrapt_timeout_decorator for timeouts. But combining them means stacking decorators, managing separate configs, and losing visibility across patterns. pyresilience unifies everything into a single @resilient() decorator with zero dependencies.
Resilience4j Feature Parity
| Resilience4j Module | pyresilience | Status |
|---|---|---|
| CircuitBreaker | CircuitBreakerConfig |
Complete |
| Retry | RetryConfig |
Complete |
| Bulkhead | BulkheadConfig |
Complete |
| TimeLimiter | TimeoutConfig |
Complete |
| RateLimiter | RateLimiterConfig |
Complete |
| Cache | CacheConfig |
Complete |
| Registry | ResilienceRegistry |
Complete |
Install
pip install pyresilience
# Optional: faster event loop + JSON serialization
pip install pyresilience[fast]
Quick Start
from pyresilience import resilient, RetryConfig, TimeoutConfig, CircuitBreakerConfig, FallbackConfig
@resilient(
retry=RetryConfig(max_attempts=3, delay=1.0, backoff_factor=2.0),
timeout=TimeoutConfig(seconds=10),
circuit_breaker=CircuitBreakerConfig(failure_threshold=5, recovery_timeout=30),
fallback=FallbackConfig(handler=lambda e: {"error": str(e), "cached": True}),
)
def call_api(endpoint: str) -> dict:
return requests.get(endpoint).json()
# Works with async too
@resilient(
retry=RetryConfig(max_attempts=3),
timeout=TimeoutConfig(seconds=5),
)
async def async_call(url: str) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.json()
Features
| Pattern | What it does |
|---|---|
| Retry | Exponential backoff with jitter, configurable exceptions |
| Timeout | Per-call time limits (thread-based sync, native async) |
| Circuit Breaker | Stop calling failing services, auto-recover via half-open |
| Fallback | Graceful degradation with static values or callables |
| Bulkhead | Concurrency limiting to prevent resource exhaustion |
| Rate Limiter | Token bucket rate limiting (calls per time window) |
| Cache | LRU result caching with TTL to avoid redundant calls |
Plus:
- Registry for centralized management of named resilience instances
- Event system for observability (
ResilienceListener) - Opinionated presets —
http_policy(),db_policy(),queue_policy(),strict_policy() - Structured logging —
JsonEventLoggerandMetricsCollector - Zero dependencies — pure Python stdlib
- Optional performance backends —
uvloop+orjsonviapip install pyresilience[fast] - Full async support — auto-detects sync vs async
- Type-safe — strict mypy compatible,
py.typedmarker - Python 3.9+
Rate Limiter
Limit call rate using a token bucket algorithm:
from pyresilience import resilient, RateLimiterConfig
@resilient(rate_limiter=RateLimiterConfig(max_calls=10, period=1.0))
def call_api(endpoint: str) -> dict:
return requests.get(endpoint).json()
# With waiting instead of immediate rejection:
@resilient(rate_limiter=RateLimiterConfig(max_calls=10, period=1.0, max_wait=5.0))
async def rate_limited_call() -> dict:
...
Cache
Cache function results with TTL and LRU eviction:
from pyresilience import resilient, CacheConfig
@resilient(cache=CacheConfig(max_size=256, ttl=300.0))
def get_user(user_id: int) -> dict:
return db.query(f"SELECT * FROM users WHERE id = {user_id}")
# Second call with same args returns cached result
get_user(42) # hits DB
get_user(42) # returns cached, DB not called
Registry
Share resilience state (circuit breakers, rate limiters) across functions:
from pyresilience import ResilienceRegistry, ResilienceConfig, RetryConfig, CircuitBreakerConfig
registry = ResilienceRegistry()
registry.register("payment-api", ResilienceConfig(
retry=RetryConfig(max_attempts=3),
circuit_breaker=CircuitBreakerConfig(failure_threshold=5),
))
@registry.decorator("payment-api")
async def charge_card(amount: float) -> dict:
...
@registry.decorator("payment-api")
async def refund_card(amount: float) -> dict:
...
# Both functions share the same circuit breaker —
# if charge_card trips the circuit, refund_card is also blocked
Presets
Opinionated defaults for common integration patterns:
from pyresilience import resilient
from pyresilience.presets import http_policy, db_policy, queue_policy
@resilient(**http_policy())
def call_api(url: str) -> dict:
return requests.get(url).json()
@resilient(**db_policy())
def query_db(sql: str) -> list:
return cursor.execute(sql).fetchall()
@resilient(**queue_policy())
async def publish_message(msg: dict) -> None:
await producer.send(msg)
Observability
from pyresilience import resilient, RetryConfig, JsonEventLogger, MetricsCollector
logger = JsonEventLogger()
metrics = MetricsCollector()
@resilient(retry=RetryConfig(max_attempts=3), listeners=[logger, metrics])
def monitored_call():
return do_work()
# After calls:
print(metrics.summary())
# {'total_events': 5, 'success_rate': 0.8, 'p99_latency': 1.23, ...}
Documentation
Full docs at pyresilience.readthedocs.io
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyresilience-0.1.0.tar.gz.
File metadata
- Download URL: pyresilience-0.1.0.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e92098e9e8ea1bd67f7c3121401ad9728048dc19104b3c596473f2802bc6701e
|
|
| MD5 |
fce3cf7b9c367da12e4b37e189523d59
|
|
| BLAKE2b-256 |
b64c48de7ac7cf2c873534e6fd51604476e3320c47bdd61cc676cd830664f79b
|
Provenance
The following attestation bundles were made for pyresilience-0.1.0.tar.gz:
Publisher:
ci.yml on AhsanSheraz/pyresilience
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyresilience-0.1.0.tar.gz -
Subject digest:
e92098e9e8ea1bd67f7c3121401ad9728048dc19104b3c596473f2802bc6701e - Sigstore transparency entry: 1127272102
- Sigstore integration time:
-
Permalink:
AhsanSheraz/pyresilience@b3bd8c66bb3fbd454d950f946fa204ee69d49d5e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/AhsanSheraz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@b3bd8c66bb3fbd454d950f946fa204ee69d49d5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyresilience-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyresilience-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a93c9cdd167e0536c98fa9fffa2f527bd99c94e39793bd052aaf2177bcabcaf1
|
|
| MD5 |
186f1ad8ec253ee46bd1da8f0e97927f
|
|
| BLAKE2b-256 |
44d744a7ab3b0dedaafd38199a5393217a87c1452ba7cde92cfef97178c8dbf0
|
Provenance
The following attestation bundles were made for pyresilience-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on AhsanSheraz/pyresilience
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyresilience-0.1.0-py3-none-any.whl -
Subject digest:
a93c9cdd167e0536c98fa9fffa2f527bd99c94e39793bd052aaf2177bcabcaf1 - Sigstore transparency entry: 1127272141
- Sigstore integration time:
-
Permalink:
AhsanSheraz/pyresilience@b3bd8c66bb3fbd454d950f946fa204ee69d49d5e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/AhsanSheraz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@b3bd8c66bb3fbd454d950f946fa204ee69d49d5e -
Trigger Event:
push
-
Statement type: