Calm, production-grade error handling & resilience for Python β decorator-based, zero boilerplate, battle-tested.
Quick Start β’ Features β’ API Reference β’ Examples β’ Comparison β’ Installation
Writing repetitive try/except blocks clutters your code and buries business logic under layers of defensive programming. Retry loops, circuit breakers, rate limits, and timeout handling are cross-cutting concerns that should not pollute your core logic.
tranq gives you declarative error handling through decorators, context managers, and a comprehensive resilience toolkit β so you focus on what your code does, not how it recovers from failure.
# β Without tranq: 30+ lines of boilerplate
import time, logging
for attempt in range(5):
try:
result = call_external_api()
break
except ConnectionError as e:
if attempt == 4: raise
logging.warning(f"Attempt {attempt+1} failed: {e}")
time.sleep(2 ** attempt)
# β
With tranq: 2 lines
import tranq
@tranq.handle(on=ConnectionError, retry=4, delay=1.0, backoff=2.0)
def call_external_api():
...| Principle | Description |
|---|---|
| π§ Tranquil | Clean, readable, maintainable. Zero boilerplate. |
| π Non-invasive | Decorators and context managers. No code changes inside functions. |
| π§© Composable | Every feature combines: retry + CB + timeout + metrics + hooks. |
| π Production-ready | Thread-safe, async-safe, contextvar-isolated. 190+ tests. |
| π¦ Zero dependencies | Core has no external deps. Optional extras for integrations. |
| π Observable | Built-in metrics, profiling, percentiles, health reports, reporters. |
import tranq
@tranq.handle(on=ValueError, retry=3, delay=0.5, backoff=2.0)
def risky_operation():
"""Retried up to 3 times on ValueError with exponential backoff."""
...@tranq.handle_async(on=ConnectionError, retry=2, fallback=lambda: "offline")
async def fetch_data():
"""Async function with fallback on failure."""
...cb = tranq.CircuitBreaker(failure_threshold=5, timeout=60)
@tranq.handle(on=Exception, circuit_breaker=cb)
def call_unstable_service():
...with tranq.retry(on=ValueError, retry=2, delay=0.1) as ctx:
result = ctx.run(my_function, arg1, kwarg1=value)async with tranq.retry_async(on=ConnectionError, retry=3) as ctx:
result = await ctx.run(my_async_function, arg1)from tranq import PolicyBuilder, wait, stop, retry_if
policy = (
PolicyBuilder()
.retry(max_attempts=3, wait=wait.exponential(0.5, max=10))
.timeout(5)
.circuit_breaker(tranq.SlidingWindowCircuitBreaker(failure_rate_threshold=0.5))
.rate_limit(rate=10)
.bulkhead(max_concurrent=5)
.fallback(lambda: {"status": "cached"})
.cache(ttl=60)
)
@policy
def call_service():
...@tranq.rate_limit(rate=10, per=1.0, burst=20)
def api_call():
"""Limited to 10 calls/second with burst of 20."""
...@tranq.bulkhead(max_concurrent=5, timeout=10.0)
def limited_operation():
"""At most 5 concurrent executions."""
...group = tranq.retry_group(step1, step2, step3, on=Exception, retry=2)
results = group.run() # All steps retried together if any failspip install tranqRequires Python 3.9 or later. The library has zero runtime dependencies.
pip install tranq[rich] # Pretty terminal logging with colors
pip install tranq[sentry] # Sentry error reporting
pip install tranq[slack] # Slack webhook notifications
pip install tranq[prometheus] # Prometheus metrics exposition
pip install tranq[all] # All integrations
pip install tranq[dev] # Development tools (pytest, black, isort, build, twine)Verify installation:
python -m tranq
# Output: tranq v1.1.0 - Calm error handling with advanced resilience features.Exponential, linear, Fibonacci, constant, random backoff. Full/equal/decorrelated jitter. Custom callable. Min/max delay cap. Composable with +.
from tranq import wait, stop
@tranq.handle(
on=TimeoutError,
retry=5,
delay=0.1,
backoff=2.0,
backoff_strategy="exponential",
max_delay=10.0,
jitter=True,
)
def fetch(): ...Hard execution limits for sync & async functions. Per-attempt and total-operation deadlines.
@tranq.handle(on=Exception, retry=2, timeout=5.0)
def slow_operation(): ...Count-based and sliding-window (failure-rate). Slow-call detection. State-change events. Breaker registry. Sync & async.
swc = tranq.SlidingWindowCircuitBreaker(
window_size=100, failure_rate_threshold=0.5, minimum_calls=10)
@tranq.handle(on=Exception, circuit_breaker=swc)
def unreliable_service(): ...Token bucket, leaky bucket, fixed window, sliding window, adaptive rate limiting.
@tranq.rate_limit(rate=10, per=1.0, burst=20)
def api_call(): ...Thread-based, async semaphore, queue-based, per-key isolation.
@tranq.bulkhead(max_concurrent=5, timeout=10.0)
def database_query(): ...Prevents retry storms under load.
budget = tranq.RetryBudget(ttl=60, ratio=0.2, min_tokens=10)
@tranq.handle(on=Exception, retry=5, retry_budget=budget)
def protected_call(): ...Race duplicate async requests, take the first success. Quorum mode. Percentile-based delay.
@tranq.hedged(hedge_delay=0.1, max_hedges=2)
async def fast_fetch(): ...Retry on specific exceptions, result values, HTTP status codes, exception chains, or Retry-After headers.
from tranq import retry_if
@tranq.handle(
on=Exception,
retry=3,
retry_if=retry_if.http_status((429, 503)) | retry_if.exception_type(ConnectionError),
)
def call_api(): ...File (JSON lines), Log, Sentry, Slack, Prometheus. Pluggable custom reporters.
reporters = [
tranq.FileReporter("errors.jsonl"),
tranq.LogReporter(),
tranq.SentryReporter(dsn="..."),
tranq.SlackReporter(webhook_url="..."),
tranq.PrometheusReporter(),
]
@tranq.handle(on=Exception, reporters=reporters)
def critical_operation(): ...Inject exceptions for testing with configurable probability and seed.
with tranq.mock_errors(ConnectionError, probability=0.8, seed=42):
result = my_function()Inject dependencies into decorated functions.
@tranq.handle(inject={"logger": logging.getLogger("app")})
def do_work(logger=None):
logger.info("Working...")Set defaults once, override per function.
tranq.set_global_policy(tranq.Policy(retry=3, delay=0.5, backoff=2.0))Automatically tunes policy based on observed latency, error rate, and traffic.
ctrl = tranq.AdaptiveController(base_timeout=10.0, base_retry=3)
ctrl.observe(latency=2.0, success=False)
rec = ctrl.recommend()
# {'timeout': 2.5, 'retry': 1, 'observed_error_rate': 1.0, ...}Ready-made configs for common backends.
from tranq import presets
@tranq.resilient(**presets.http())
def fetch_users(): ...
@tranq.resilient(**presets.database())
def run_query(): ...
# Available: http, database, redis, kafka, queue, grpc, llmAutomatic retryable status detection and Retry-After parsing.
from tranq import is_retryable, parse_retry_after, recommended_wait
if is_retryable(exception):
wait_seconds = recommended_wait(exception, default=1.0)Automatic spans and counters for resilience operations. Graceful no-op when not installed.
@tranq.telemetry(service="payment")
def process_payment(): ...Subscribe to 14 resilience lifecycle events.
bus = tranq.EventBus()
bus.subscribe(tranq.events.CircuitOpened, lambda e: alert(f"CB opened: {e.data}"))
bus.subscribe("*", lambda e: log(e)) # wildcardEvents: RetryStarted, RetryCompleted, CircuitOpened, CircuitClosed, CircuitHalfOpened, TimeoutTriggered, RateLimitExceeded, BulkheadRejected, FallbackTriggered, CacheHit, CacheMiss, HedgeStarted, OperationSucceeded, OperationFailed.
Pluggable state backends (in-memory, Redis) for cluster-wide rate limits, circuit breakers, and budgets.
from tranq import RedisBackend, DistributedRateLimiter
backend = RedisBackend(url="redis://localhost:6379/0")
limiter = DistributedRateLimiter(limit=100, window=1.0, backend=backend)Track health of named dependencies and aggregate service health.
graph = tranq.DependencyGraph()
graph.register("api", dependencies=["postgres", "redis", "payment"])
graph.record("payment", success=False)
print(graph.failing_dependencies("api")) # ["payment"]Full resilience diagnostics with recommended actions.
print(tranq.render_diagnostics())
# SERVICE HEALTH
# ββββββββββββββββββββββββββββββββ
# API HEALTHY
# Payment DEGRADED
# Overall: DEGRADED (calls=500, errors=12)
# Recommended actions:
# - Enable jitter to avoid thundering herds
# - A circuit breaker is open; investigate downstreamInject latency, errors, and timeouts for resilience testing.
with tranq.chaos(latency=0.3, error_rate=0.2, timeout_rate=0.1) as inj:
for _ in range(100):
inj.maybe_inject()
call_service()
print(tranq.chaos_report())Fluent builder composing all resilience features into a single decorator.
from tranq import PolicyBuilder, wait, stop, retry_if
policy = (
PolicyBuilder()
.retry(max_attempts=3, wait=wait.full_jitter(0.5, max=10),
stop=stop.after_attempt(3) | stop.after_delay(30))
.timeout(5)
.circuit_breaker(tranq.SlidingWindowCircuitBreaker(...))
.rate_limit(rate=10)
.bulkhead(max_concurrent=5)
.fallback(tranq.FallbackChain(cached_fallback, static_fallback))
.cache(ttl=60)
.observe(event_bus=bus)
)
@policy
def call_service(): ...Rate-limit handling, Retry-After, provider/model fallback, automatic failover.
@tranq.llm(max_attempts=5, providers=[anthropic_client, local_model])
async def ask(prompt): ...Detect operation type and recommend/apply a policy automatically.
@tranq.auto(verbose=True) # Recommends only (safe by default)
async def fetch_orders(): ...
@tranq.auto(apply=True) # Actually applies the recommended policy
async def fetch_users(): ...Counts, error-rate, min/max/avg, p50/p95/p99 latencies, summary tables, health reports.
@tranq.handle(metrics=True, metric_prefix="svc")
def expensive_op(): ...
print(tranq.summary_table())
print(tranq.overall_health())TTL, LRU eviction, stampede prevention (single-flight), stale-if-error, negative caching, cache metrics.
@tranq.cache(ttl=60, maxsize=256)
def expensive_computation(x): ...Static, callable, async, chain, cached (stale-while-failing), conditional.
chain = tranq.FallbackChain(cached_result, redis_fallback, static_default)
@PolicyBuilder().fallback(chain)
def get_data(): ...| Function | Description |
|---|---|
@tranq.handle(...) |
Sync decorator with full error handling |
@tranq.handle_async(...) |
Async decorator (same parameters) |
@tranq.rate_limit(rate, per, burst, timeout) |
Token-bucket rate limiter |
@tranq.bulkhead(max_concurrent, timeout) |
Concurrency limiter |
@tranq.hedged(hedge_delay, max_hedges) |
Hedged requests (async) |
@tranq.profile / @tranq.async_profile |
Execution time measurement |
@tranq.cache(ttl, maxsize) |
Cache-aside decorator |
@tranq.telemetry(service) |
OpenTelemetry spans |
@tranq.llm(max_attempts, providers) |
LLM resilience |
@tranq.auto(apply, verbose) |
Auto policy detection |
@tranq.resilient(...) |
Shortcut for PolicyBuilder |
| Function | Description |
|---|---|
tranq.retry(...) |
Sync context manager |
tranq.retry_async(...) |
Async context manager |
| Module | Key Classes/Functions |
|---|---|
tranq.wait |
fixed, none, random, exponential, fibonacci, incrementing, full_jitter, equal_jitter, decorrelate_jitter, combine |
tranq.stop |
after_attempt, after_delay, before_deadline, never, when, all (&), any (|) |
tranq.retry_if |
exception_type, not_exception_type, exception, result, http_status, exception_chain, retry_after, all (&), any (|) |
| Class | Description |
|---|---|
CircuitBreaker |
Count-based sync (+ slow-call, events, reset) |
AsyncCircuitBreaker |
Count-based async |
SlidingWindowCircuitBreaker |
Failure-rate sync |
AsyncSlidingWindowCircuitBreaker |
Failure-rate async |
BreakerRegistry |
Shared named breakers |
| Class | Algorithm |
|---|---|
RateLimiter |
Token bucket |
LeakyBucket |
Leaky bucket |
FixedWindowLimiter |
Fixed window counter |
SlidingWindowLimiter |
Sliding window log |
AdaptiveRateLimiter |
Auto-adjusting token bucket |
DistributedRateLimiter |
Cluster-wide fixed window |
| Class | Description |
|---|---|
Bulkhead |
Thread semaphore |
AsyncBulkhead |
Asyncio semaphore |
QueueBulkhead |
Bounded queue + worker pool |
KeyedBulkhead |
Per-key isolation |
| Component | Description |
|---|---|
RetryBudget |
Token-based retry storm prevention |
TranqCache / AsyncTranqCache |
TTL + LRU + stampede prevention |
FallbackChain |
Sequential fallback attempts |
CachedFallback |
Stale-while-failing |
ConditionalFallback |
Exception-type-based fallback |
EventBus |
Pub/sub for 14 event types |
AdaptiveController |
Auto-tuning from observed signals |
DependencyGraph |
Dependency health tracking |
ChaosInjector |
Latency/error/timeout injection |
Telemetry |
OpenTelemetry integration |
Deadline |
Absolute deadline with propagation |
PolicyBuilder |
Fluent policy composition |
presets |
http/database/redis/kafka/grpc/llm |
| Exception | Description |
|---|---|
TranqError |
Base class |
RetryExhaustedError |
All retries exhausted |
CircuitBreakerError |
Circuit breaker open |
ResultNotAcceptedError |
retry_on_result rejected final result |
RetryGroupError |
Retry group member failed |
FunctionTimeoutError |
Timeout exceeded (also TimeoutError) |
RateLimitExceeded |
Rate limiter rejected call |
BulkheadFullError |
Bulkhead at capacity |
RetryBudgetExhaustedError |
Retry budget empty |
DeadlineExceededError |
Overall deadline passed |
| Function | Description |
|---|---|
get_metrics() / reset_metrics() |
Metrics collection |
summary_table() / overall_health() / render_report() |
Statistics |
diagnostics() / render_diagnostics() |
Health analysis |
chaos(...) / chaos_report() |
Chaos testing |
mock_errors(...) |
Error injection |
set_global_policy() / get_global_policy() |
Global defaults |
is_retryable() / parse_retry_after() |
HTTP intelligence |
recommend_policy() |
Auto policy detection |
The examples/ directory contains 38 complete, runnable scripts:
| File | Topic |
|---|---|
01_basic_decorator.py |
Basic @handle usage |
02_retry_and_backoff.py |
All backoff strategies |
03_conditional_retry.py |
retry_if |
04_retry_on_result.py |
Retry on return value |
05_error_handlers.py |
Multiple on_error handlers |
06_fallback.py |
Fallback values/functions |
07_circuit_breaker.py |
Sync circuit breaker |
08_async_circuit_breaker.py |
Async circuit breaker |
09_context_manager.py |
with tranq.retry(...) |
10_retry_group.py |
Sync retry group |
11_async_retry_group.py |
Async retry group |
12_metrics.py |
Metrics collection |
13_profiling.py |
Function profiling |
14_reporters.py |
File/custom reporters |
15_mock_errors.py |
Mock error injection |
16_dependency_injection.py |
inject parameter |
17_stateful_retry.py |
Stateful retry |
18_global_policy.py |
Global policy |
19_async_decorator.py |
@handle_async |
20_combined_advanced.py |
Everything combined |
21_timeout.py |
Sync/async timeout |
22_event_hooks.py |
Lifecycle hooks |
23_rate_limiter.py |
Token bucket rate limiting |
24_bulkhead.py |
Concurrency isolation |
25_retry_budget.py |
Retry storm prevention |
26_hedged_requests.py |
Hedged async requests |
27_sliding_window_cb.py |
Failure-rate circuit breaker |
28_statistics_report.py |
Summary tables & health |
29_async_retry_context.py |
async with tranq.retry_async |
30_wait_stop.py |
Composable wait/stop |
31_cache.py |
Cache with stampede prevention |
32_policy_builder.py |
Policy composition DSL |
33_adaptive.py |
Adaptive resilience |
34_chaos.py |
Chaos testing |
35_presets.py |
Policy presets |
36_event_bus.py |
Unified event bus |
37_diagnostics.py |
Health analysis |
38_auto_llm.py |
Auto detection & LLM resilience |
python examples/run_all.py # Run all 38 examples
python examples/07_circuit_breaker.py # Run a single exampleThe test suite contains 190+ tests covering all features across sync, async, and thread-safety:
pip install -e ".[dev]"
pytest tests/ -vTest categories: circuit breakers (count + sliding window), all backoff strategies, jitter, conditional retry, error handlers, fallback, dependency injection, stateful retry, retry groups, reporters, metrics, profiling, mock errors, global policy, timeout, event hooks, rate limiters (5 algorithms), bulkhead, retry budget, hedged requests, cache, chaos testing, presets, diagnostics, event bus, wait/stop/retry_if composition, policy builder.
| Feature | tranq | tenacity | backoff | pyresilience |
|---|---|---|---|---|
| Decorator (sync + async) | β | β | β | β |
| Circuit Breaker | β | β | β | β |
| Sliding Window CB | β | β | β | β |
| Rate Limiter (5 algorithms) | β | β | β | β |
| Bulkhead (4 types) | β | β | β | β |
| Retry Budget | β | β | β | β |
| Hedged Requests | β | β | β | β |
| Timeout | β | β | β | |
| Event Hooks | β | β | ||
| Context Manager | β | β | β | β |
| Async Context Manager | β | β | β | β |
| Retry Groups | β | β | β | β |
| Metrics + Percentiles | β | β | β | β |
| Statistics / Health | β | β | β | β |
| Reporters (5 built-in) | β | β | β | β |
| Mock Errors / Chaos | β | β | β | β |
| Dependency Injection | β | β | β | β |
| Global Policy | β | β | β | β |
| Stateful Retry | β | β | β | β |
| Cache + Stampede Prevention | β | β | β | β |
| Policy Composition DSL | β | β | β | β |
| Adaptive Resilience | β | β | β | β |
| Policy Presets | β | β | β | β |
| HTTP Intelligence | β | β | β | β |
| OpenTelemetry Native | β | β | β | β |
| Event Bus | β | β | β | β |
| Distributed State | β | β | β | β |
| Dependency Graph | β | β | β | β |
| LLM Resilience | β | β | β | β |
| Auto Policy Detection | β | β | β | β |
| Zero Dependencies | β | β | β | β |
tranq/
βββ .github/workflows/
β βββ ci.yml # Test matrix (5 Python Γ 3 OS)
β βββ release.yml # Auto-publish to PyPI + GitHub Release
βββ docs/source/
β βββ conf.py
β βββ index.rst # Full Sphinx documentation
βββ examples/ # 38 runnable examples
β βββ 01_basic_decorator.py
β βββ ...
β βββ 38_auto_llm.py
β βββ run_all.py
βββ src/tranq/
β βββ __init__.py # Public API (101 exports)
β βββ __main__.py # python -m tranq
β βββ decorators.py # @handle / @handle_async
β βββ context.py # retry() / retry_async()
β βββ composition.py # PolicyBuilder / ResiliencePolicy
β βββ circuit_breaker.py # Count-based CB + slow-call + registry
β βββ async_circuit_breaker.py # Async count-based CB
β βββ sliding_window.py # Failure-rate CB (sync + async)
β βββ rate_limiter.py # 5 rate limiting algorithms
β βββ bulkhead.py # 4 bulkhead types
β βββ retry_budget.py # Retry storm prevention
β βββ hedge.py # Hedged requests + quorum
β βββ cache.py # TTL/LRU/stampede/stale-if-error
β βββ fallback.py # Chain/cached/conditional fallback
β βββ wait.py # 10 composable wait strategies
β βββ stop.py # Composable stop conditions
β βββ retry_predicates.py # Composable retry predicates
β βββ timeout.py # Deadline + per-attempt timeout
β βββ events.py # Unified event bus (14 events)
β βββ adaptive.py # Adaptive resilience controller
β βββ presets.py # http/db/redis/kafka/grpc/llm
β βββ http_intel.py # HTTP status + Retry-After
β βββ diagnostics.py # Health analysis + recommendations
β βββ chaos.py # Chaos testing framework
β βββ telemetry.py # OpenTelemetry integration
β βββ distributed.py # Distributed state backends
β βββ dependency.py # Dependency health graph
β βββ llm.py # LLM resilience + provider failover
β βββ auto.py # Auto policy detection
β βββ policies.py # Policy dataclass + global policy
β βββ exceptions.py # Exception hierarchy
β βββ metrics.py # Metrics + percentiles
β βββ statistics.py # Summary tables + health
β βββ profiling.py # Function profiling
β βββ reporters.py # File/Log/Sentry/Slack/Prometheus
β βββ mock.py # Error injection
β βββ retry_group.py # All-or-nothing groups
β βββ utils.py # Backoff, jitter, logging
β βββ py.typed # PEP 561 marker
βββ tests/ # 190+ tests
βββ CHANGELOG.md
βββ CONTRIBUTING.md
βββ LICENSE
βββ pyproject.toml
βββ pytest.ini
βββ README.md
- Always use jitter β prevents thundering herd on simultaneous retries.
- Set max_delay β caps exponential backoff to prevent unbounded waits.
- Use retry budgets for high-traffic services β prevents retry storms.
- Combine circuit breaker + sliding window β tolerate sporadic failures.
- Add observability β metrics + reporters + event bus for every critical path.
- Use timeouts for external calls β never let them hang indefinitely.
- Graceful degradation β always provide a fallback for user-facing operations.
Contributions are welcome! Please see CONTRIBUTING.md.
# Setup
pip install -e ".[dev]"
# Test
pytest tests/ -v
# Format
black src/ tests/ && isort src/ tests/See CHANGELOG.md for full release history.
Latest: v1.1.0 β Resilience Platform release with composable retry primitives, cache with stampede prevention, 5 rate limiting algorithms, 4 bulkhead types, policy composition DSL, adaptive resilience, presets, HTTP intelligence, OpenTelemetry, event bus, distributed state, dependency graph, chaos testing, LLM resilience, auto policy detection, and 190+ tests.
MIT Β© RaptorVampire