Skip to main content

Resilient decorators that return Result types instead of throwing exceptions

Project description

resilient-result

PyPI version Python 3.8+ License: MIT

Resilience mechanisms with Result types.

from resilient_result import resilient, Result

# Simple: perfect defaults for most cases
@resilient()
async def call_api(url: str) -> str:
    return await http.get(url)

# Advanced: compose individual mechanisms
@timeout(seconds=10)
@retry(attempts=5)  
async def robust_call(url: str) -> str:
    return await http.get(url)

result: Result[str, Exception] = await call_api("https://api.example.com")
if result.success:
    data = result.unwrap()  # Extract data safely
    print(data)
else:
    print(f"Failed: {result.error}")  # Inspect error directly

Why resilient-result? Network calls fail. Databases timeout. APIs rate limit. Handle it cleanly without exception soup.

Observability built-in: Enable logging.getLogger('resilient_result').setLevel(logging.DEBUG) to see retry attempts and recovery.

📖 Result API | 🔧 Resilience Patterns

Installation

pip install resilient-result

Core Features

Progressive Disclosure

from resilient_result import resilient, retry, timeout, circuit, rate_limit

# Simple: Just works with reasonable defaults
@resilient()  # 2 attempts, 1s backoff, 30s timeout
async def simple_operation():
    return await external_service()

# Advanced: Compose individual mechanisms
@rate_limit(rps=100)         # Rate limiting (100 rps default)
@circuit(failures=3)         # Circuit breaker (1 minute window)
@timeout(seconds=10)         # Time-based protection
@retry(attempts=5)           # Retry mechanism
async def critical_operation():
    return await external_service()

Result Usage

from resilient_result import Result, Ok, Err

# Pattern 1: Check then unwrap
result = await call_api("https://api.example.com")
if result.success:
    data = result.unwrap()
    process(data)

# Pattern 2: Error inspection for conditional logic
result = await call_api("https://api.example.com")
if result.failure and "rate_limit" in result.error:
    await asyncio.sleep(60)  # Backoff on rate limit
    retry()
elif result.failure:
    log_error(result.error)

# Pattern 3: Direct unwrap (raises exception on failure)
try:
    data = result.unwrap()
    process(data)
except ApiError as e:
    log_error(e)

Policy Configuration

from resilient_result import Retry, Backoff, Circuit, resilient

# Explicit policy configuration
@resilient(
    retry=Retry(attempts=5, timeout=60),
    backoff=Backoff.exp(delay=0.1, max_delay=10),  # Jitter enabled by default
    circuit=Circuit(failures=3, window=60)  # 1 minute window
)
async def custom_operation():
    return await external_service()

# Shorthand for common patterns
@resilient(retry=Retry(attempts=5))  # Just more attempts
@resilient(timeout=60)               # Just longer timeout
async def database_operation():
    return await db.query()

Parallel Operations

from resilient_result import Result

# Collect multiple async operations
operations = [fetch_user(1), fetch_user(2), fetch_user(3)]
result = await Result.collect(operations)

if result.success:
    users = result.unwrap()  # All succeeded
else:
    try:
        result.unwrap()  # Raises first failure
    except Exception as e:
        print(f"First failure: {e}")

Error Inspection Patterns

# Canonical API: 3 ways to work with Results
result = await call_api("https://api.example.com")

# 1. Status checking
if result.success:
    print("Success!")
if result.failure:
    print("Failed!")

# 2. Error inspection (without exceptions)
if result.failure:
    error_msg = result.error
    if "network" in str(error_msg):
        retry_with_backoff()
    elif "auth" in str(error_msg):
        refresh_token()

# 3. Value extraction (raises on failure)
try:
    data = result.unwrap()  
    process(data)
except Exception as e:
    handle_error(e)

License

MIT - Build amazing resilient systems! 🚀


🗺️ Roadmap

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

resilient_result-0.4.1.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

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

resilient_result-0.4.1-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file resilient_result-0.4.1.tar.gz.

File metadata

  • Download URL: resilient_result-0.4.1.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.12.10 Darwin/24.5.0

File hashes

Hashes for resilient_result-0.4.1.tar.gz
Algorithm Hash digest
SHA256 6f7f3d8f1e513058790ba415ed519e82f81d1e4aa2ed4f32a16ee158ea3fa8c7
MD5 7858b15bd2da8678a06b09f32e1fee36
BLAKE2b-256 68190dea2f5095d26aa6d4aa496d1945531893b7d8eaf9f2691ce410c0648376

See more details on using hashes here.

File details

Details for the file resilient_result-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: resilient_result-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.12.10 Darwin/24.5.0

File hashes

Hashes for resilient_result-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 31edb802ea60a313c7fbf15a501bc2aec023794a2b64ffa5e7424684687603df
MD5 328e7c6a024727bfcd5e89be52c493e9
BLAKE2b-256 dcb0abf94b578d042ba11936c2239746c8ee835007e86e35b78f225cf09d0612

See more details on using hashes here.

Supported by

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