Skip to content
RelPrim docsGetting started

Overview

Getting started

Install RelPrim, retry a failing async call, read its execution report, then add a timeout and a fallback.

This page takes about five minutes. It uses the @resilient decorator, which covers most integrations; the builder API offers the same features with explicit composition.

Install

pip install relprim

RelPrim requires Python 3.11 or later and has no runtime dependencies.

Retry a failing call

The example below is complete and runs as-is. The operation fails twice with a transient error, then succeeds:

import asyncio

from relprim import resilient


class TemporaryError(Exception):
    pass


calls = 0


@resilient(retries=3, retry_on=(TemporaryError,))
async def fetch_quote(symbol: str) -> str:
    global calls
    calls += 1
    if calls < 3:
        raise TemporaryError("upstream is busy")
    return f"{symbol}: 101.25"


async def main() -> None:
    result = await fetch_quote("ACME")
    print(result.value)
    print(result.report.attempt_count, result.report.retry_count)


asyncio.run(main())
ACME: 101.25
3 2
  • retries=3 allows up to three retries after the first attempt, so at most four attempts. Retry explains how this maps to RetryPolicy(max_attempts=...).
  • retry_on=(TemporaryError,) retries only that exception. Without it, every Exception is retried. Prefer naming the transient errors you expect.
  • Between attempts RelPrim waits with exponential backoff and jitter, starting at 0.1 seconds.

Read the result

A decorated function returns an OperationResult, not the raw value. It holds the business value and the execution report side by side:

result = await fetch_quote("ACME")

result.value                    # "ACME: 101.25"
result.report.status            # ExecutionStatus.SUCCEEDED
result.report.attempt_count     # 3
result.report.retry_count       # 2
result.report.last_error        # ExecutionError of the last failed attempt, or None
result.report.attempts          # one ExecutionAttempt per attempt
result.report.to_dict()         # JSON-ready dict for logs and storage

Every attempt records its number, status, start time, duration, error and metadata. Reports contain only JSON-compatible values, so to_dict() can be logged or stored directly. Core concepts describes the report in full.

Handle failure

When the last attempt fails and nothing else can recover, the call raises OperationExecutionError. It carries the final failure and the same execution report:

from relprim import OperationExecutionError

try:
    result = await fetch_quote("ACME")
except OperationExecutionError as error:
    print(error.cause)               # the final exception
    print(error.report.to_dict())    # what happened before it

Cancellation is never converted into a RelPrim error. If the calling task is cancelled, asyncio.CancelledError propagates immediately, without retries or fallbacks.

Add a timeout

timeout bounds each attempt, in seconds:

@resilient(retries=2, timeout=5)
async def fetch_quote(symbol: str) -> str:
    return await market_data.quote(symbol)

An attempt that exceeds five seconds is cancelled and fails with OperationTimeoutError, which is retried like any other failure that matches retry_on. All durations in RelPrim are seconds. See Timeouts for what exactly is timed.

Add a fallback

A fallback is another async function with the same parameters. It runs once, after the primary attempts are exhausted:

async def cached_quote(symbol: str) -> str:
    return await quote_cache.get(symbol)


@resilient(retries=2, timeout=5, fallback=cached_quote)
async def fetch_quote(symbol: str) -> str:
    return await market_data.quote(symbol)


result = await fetch_quote("ACME")
result.report.metadata["fallback_used"]   # True when cached_quote produced the value

The fallback is recorded as one extra attempt and does not count as a retry. Fallbacks covers chains with several candidates.

Next steps

  • Core concepts: the model behind every operation and the order in which policies apply.
  • Validation: reject results that are well-formed calls but unusable values.
  • Idempotency: coordinate duplicate calls for the same logical request.
  • Structured events: observe executions as they happen.

Ready for complete scenarios? Examples shows runnable code for provider failover, invalid responses, duplicate payments, Retry-After, circuit breakers and observability.