Skip to main content

🌿 tranq

Calm, production-grade error handling & resilience for Python — decorator-based, zero boilerplate, battle-tested.

PyPI Version Python Versions CI Status License GitHub

Quick StartFeaturesAPI ReferenceExamplesComparisonInstallation


🧘 Why tranq?

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():
    ...

Design Principles

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.

⚡ Quick Start

Decorator (@handle)

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."""
    ...

Async Decorator (@handle_async)

@tranq.handle_async(on=ConnectionError, retry=2, fallback=lambda: "offline")
async def fetch_data():
    """Async function with fallback on failure."""
    ...

Circuit Breaker

cb = tranq.CircuitBreaker(failure_threshold=5, timeout=60)

@tranq.handle(on=Exception, circuit_breaker=cb)
def call_unstable_service():
    ...

Context Manager (Sync)

with tranq.retry(on=ValueError, retry=2, delay=0.1) as ctx:
    result = ctx.run(my_function, arg1, kwarg1=value)

Async Context Manager

async with tranq.retry_async(on=ConnectionError, retry=3) as ctx:
    result = await ctx.run(my_async_function, arg1)

Policy Composition DSL

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():
    ...

Rate Limiter

@tranq.rate_limit(rate=10, per=1.0, burst=20)
def api_call():
    """Limited to 10 calls/second with burst of 20."""
    ...

Bulkhead (Concurrency Limit)

@tranq.bulkhead(max_concurrent=5, timeout=10.0)
def limited_operation():
    """At most 5 concurrent executions."""
    ...

Retry Group (All-or-Nothing)

group = tranq.retry_group(step1, step2, step3, on=Exception, retry=2)
results = group.run()  # All steps retried together if any fails

📦 Installation

pip install tranq

Requires Python 3.9 or later. The library has zero runtime dependencies.

Optional Extras

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.

🔥 Features

Layer 1 — Competitor Parity

🔁 Smart Retries

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(): ...

⏱️ Timeouts

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(): ...

🚦 Circuit Breakers

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(): ...

🚦 Rate Limiting

Token bucket, leaky bucket, fixed window, sliding window, adaptive rate limiting.

@tranq.rate_limit(rate=10, per=1.0, burst=20)
def api_call(): ...

🚪 Bulkhead Isolation

Thread-based, async semaphore, queue-based, per-key isolation.

@tranq.bulkhead(max_concurrent=5, timeout=10.0)
def database_query(): ...

💰 Retry Budget

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(): ...

🏇 Hedged Requests

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(): ...

🧪 Conditional Retry

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(): ...

📝 Reporters

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(): ...

🎭 Mock Error Injection

Inject exceptions for testing with configurable probability and seed.

with tranq.mock_errors(ConnectionError, probability=0.8, seed=42):
    result = my_function()

💉 Dependency Injection

Inject dependencies into decorated functions.

@tranq.handle(inject={"logger": logging.getLogger("app")})
def do_work(logger=None):
    logger.info("Working...")

🌐 Global Policy

Set defaults once, override per function.

tranq.set_global_policy(tranq.Policy(retry=3, delay=0.5, backoff=2.0))

Layer 2 — What Makes tranq Different

🧠 Adaptive Resilience

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, ...}

🎯 Policy Presets

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, llm

🌐 HTTP Intelligence

Automatic 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)

🔭 OpenTelemetry Native

Automatic spans and counters for resilience operations. Graceful no-op when not installed.

@tranq.telemetry(service="payment")
def process_payment(): ...

📡 Unified Event Bus

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))  # wildcard

Events: RetryStarted, RetryCompleted, CircuitOpened, CircuitClosed, CircuitHalfOpened, TimeoutTriggered, RateLimitExceeded, BulkheadRejected, FallbackTriggered, CacheHit, CacheMiss, HedgeStarted, OperationSucceeded, OperationFailed.

🧬 Distributed Resilience

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)

🕸️ Dependency-Aware Resilience

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"]

🩺 Automatic Health Analysis

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 downstream

🧪 Chaos Testing Framework

Inject 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())

🧩 Policy Composition DSL

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(): ...

🤖 LLM Resilience

Rate-limit handling, Retry-After, provider/model fallback, automatic failover.

@tranq.llm(max_attempts=5, providers=[anthropic_client, local_model])
async def ask(prompt): ...

🏆 Auto Policy Detection

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(): ...

📊 Advanced Metrics & Statistics

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())

🗄️ Resilience Cache

TTL, LRU eviction, stampede prevention (single-flight), stale-if-error, negative caching, cache metrics.

@tranq.cache(ttl=60, maxsize=256)
def expensive_computation(x): ...

🔄 Fallback Strategies

Static, callable, async, chain, cached (stale-while-failing), conditional.

chain = tranq.FallbackChain(cached_result, redis_fallback, static_default)

@PolicyBuilder().fallback(chain)
def get_data(): ...

📚 API Reference

Decorators

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

Context Managers

Function Description
tranq.retry(...) Sync context manager
tranq.retry_async(...) Async context manager

Retry Primitives

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 (|)

Circuit Breakers

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

Rate Limiters

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

Bulkheads

Class Description
Bulkhead Thread semaphore
AsyncBulkhead Asyncio semaphore
QueueBulkhead Bounded queue + worker pool
KeyedBulkhead Per-key isolation

Other Components

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

Exceptions

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

Utilities

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

📁 Examples

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 example

🧪 Testing

The test suite contains 190+ tests covering all features across sync, async, and thread-safety:

pip install -e ".[dev]"
pytest tests/ -v

Test 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.


⚖️ Comparison

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

🗂️ Project Structure

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

🏭 Production Best Practices

  1. Always use jitter — prevents thundering herd on simultaneous retries.
  2. Set max_delay — caps exponential backoff to prevent unbounded waits.
  3. Use retry budgets for high-traffic services — prevents retry storms.
  4. Combine circuit breaker + sliding window — tolerate sporadic failures.
  5. Add observability — metrics + reporters + event bus for every critical path.
  6. Use timeouts for external calls — never let them hang indefinitely.
  7. Graceful degradation — always provide a fallback for user-facing operations.

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md.

# Setup
pip install -e ".[dev]"

# Test
pytest tests/ -v

# Format
black src/ tests/ && isort src/ tests/

📋 Changelog

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.


📄 License

MIT © RaptorVampire

Release files for tranq 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tranq 1.1.0
File Size Uploaded
tranq-1.1.0.tar.gz 82.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tranq 1.1.0
File Interpreter ABI Platform
tranq-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 139.7 kB

Release files / tranq-1.1.0.tar.gz

Download URL tranq-1.1.0.tar.gz
Size 82.1 kB
Tags Source
SHA-256 checksum
How to use checksums
819ac824454a2244da94e1e4cc7533bfe0dfb6717b53079c58f2df0513ed14ee
BLAKE2b-256 checksum
How to use checksums
f7e299026123d2a73f763f01fa9e0c706a067c6d1d206dc0e944bdfdcb9e1623
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release files / tranq-1.1.0-py3-none-any.whl

Download URL tranq-1.1.0-py3-none-any.whl
Size 57.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d1957ec542d0627074708dba629f180d10d1cebd643028a90081f79a2f4e6e7f
BLAKE2b-256 checksum
How to use checksums
8ff68b2f067d9aaebb8b1cb9cb53f7c84457fdf2a4782c0288535e019f864917
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.0

2 release files

0.3.0

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page