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_authbearer dependency and acors_allowlistfactory 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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
badf8bcefef5a492d74908874b3ada7fc03e8d2257e827a53cd0b132adee5f96
|
|
| MD5 |
86043743b2aaacfedd22c6fdfc7aa99e
|
|
| BLAKE2b-256 |
bc9fcc1126e90cf71ff92b1cb9116ca88f8e7a1af96b2c7b4466df660a2580c6
|
File details
Details for the file cubiczan_resilience-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cubiczan_resilience-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f2960543ca3d165d3ce780b3d4887f08c78586d86f6ad65f4c0372246f7d128
|
|
| MD5 |
a040a3ff83730ab751527206fb11c7d0
|
|
| BLAKE2b-256 |
8fe4e45886a091082219a551017536cb52e48452b3123f7302ce266e056dbd1a
|