Skip to main content

cubiczan-resilience (Python)

Battle-tested resilience primitives, lifted and generalised from production services (CFO resilience matrix, Strata AWS-native, Hermes Pi factory guardian, Valiron advisory AI):

  • @resilient(...) — mandatory timeout + exponential backoff with full jitter
    • pluggable circuit breaker, for sync and async callables.
  • CircuitBreaker — standalone CLOSED / OPEN / HALF_OPEN breaker.
  • IdempotencyStore — protocol + in-memory and file-backed impls to guard money/state operations against double-execution on retry.
  • atomic_write(path, data) — write-to-temp + os.replace, never a partial file.
  • AuditLedger / verify_ledger — signed, append-only JSONL audit ledger with HMAC-SHA256 signature chaining (see the Audit Ledger section for the shared scheme).
  • FastAPI helpers — fail-closed require_auth bearer dependency and a cors_allowlist factory that forbids wildcard-origin + credentials.

Pure stdlib core. httpx and fastapi are optional extras.

Install

pip install cubiczan-resilience            # core only
pip install 'cubiczan-resilience[fastapi]' # + FastAPI helpers
pip install 'cubiczan-resilience[http]'    # + httpx

Requires Python >= 3.10.

@resilient

from cubiczan_resilience import resilient, CircuitBreaker

breaker = CircuitBreaker("payments-api", failure_threshold=5, cooldown_seconds=30)

@resilient(
    timeout=2.0,              # mandatory per-attempt deadline (seconds)
    max_attempts=4,
    base_delay=0.1,           # full-jitter backoff: U(0, base * 2**attempt)
    max_delay=10.0,
    retryable_exceptions=(ConnectionError, TimeoutError),
    circuit_breaker=breaker,  # optional; gates + records outcomes
)
def call_api() -> dict:
    ...

# Async works the same way; the timeout is hard-enforced via asyncio.wait_for.
@resilient(timeout=5.0, max_attempts=3)
async def call_api_async() -> dict:
    ...

Retry decisions: if the raised exception exposes an HTTP status (httpx/requests style .response.status_code or a bare .status_code), it is retried only when the code is in retryable_status (default {408, 425, 429, 500, 502, 503, 504}). Otherwise the exception type is matched against retryable_exceptions. CircuitOpenError is never retried.

CircuitBreaker (standalone)

from cubiczan_resilience import CircuitBreaker, CircuitOpenError

cb = CircuitBreaker("db", failure_threshold=3, cooldown_seconds=15)

if cb.allow():
    try:
        result = do_query()
    except Exception:
        cb.record_failure()
        raise
    else:
        cb.record_success()
else:
    raise CircuitOpenError(cb.name, cb.retry_after())

Idempotency

from cubiczan_resilience import FileIdempotencyStore

store = FileIdempotencyStore("/var/lib/app/idempotency.json")

def charge(order_id: str, amount: int) -> str:
    if store.already_done(order_id):
        return store.get_result(order_id)          # replay prior result
    if not store.mark_done(order_id, "charged"):    # atomic first-writer-wins claim
        return store.get_result(order_id)
    provider.charge(amount)                          # runs exactly once
    return "charged"

InMemoryIdempotencyStore has the same interface for tests / single-process use.

Atomic writes

from cubiczan_resilience import atomic_write

atomic_write("/var/lib/app/state.json", json_payload)   # str or bytes
atomic_write("/var/lib/app/secret", token, mode=0o600)  # set perms atomically

A crash before the rename leaves the previous file fully intact — never a half-written file.

FastAPI helpers

from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from cubiczan_resilience.fastapi_helpers import require_auth, cors_allowlist

app = FastAPI()
app.add_middleware(CORSMiddleware, **cors_allowlist(["https://app.example.com"]))

auth = require_auth(env_var="API_TOKEN")  # fail-closed: 503 if env var unset

@app.get("/secure")
def secure(_: str = Depends(auth)):
    return {"ok": True}

Development

pip install -e '.[dev]'
pytest
mypy src

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cubiczan_resilience-0.1.0.tar.gz (21.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cubiczan_resilience-0.1.0-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file cubiczan_resilience-0.1.0.tar.gz.

File metadata

  • Download URL: cubiczan_resilience-0.1.0.tar.gz
  • Upload date:
  • Size: 21.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for cubiczan_resilience-0.1.0.tar.gz
Algorithm Hash digest
SHA256 badf8bcefef5a492d74908874b3ada7fc03e8d2257e827a53cd0b132adee5f96
MD5 86043743b2aaacfedd22c6fdfc7aa99e
BLAKE2b-256 bc9fcc1126e90cf71ff92b1cb9116ca88f8e7a1af96b2c7b4466df660a2580c6

See more details on using hashes here.

File details

Details for the file cubiczan_resilience-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for cubiczan_resilience-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7f2960543ca3d165d3ce780b3d4887f08c78586d86f6ad65f4c0372246f7d128
MD5 a040a3ff83730ab751527206fb11c7d0
BLAKE2b-256 8fe4e45886a091082219a551017536cb52e48452b3123f7302ce266e056dbd1a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page