Result
The strict Sum Type for functional error handling.
result.result
Result: Functional Error Handling for Modern Python.
A lightweight, single-file library designed to implement the 'Errors as Values' pattern in Python 3.14+. This library tries to help bridge the gap between pure functional safety and the pragmatic realities of the exception-heavy Python ecosystem.
Core Philosophy
In standard Python, errors are implicit side-effects (Exceptions). In this library, errors are explicit return values. This forcing function ensures that failure paths are handled as diligently as success paths.
Core Goals
- Explicit Error Handling: Shift from 'invisible' exceptions to explicit
Resultreturn types, making failure modes a first-class part of the API. - Reduced Cognitive Load: Use the
@doand@do_asyncdecorators to write linear, procedural-looking code that automatically handles short-circuiting logic. - Static Analysis First: Leverage modern Python typing features (PEP 695, PEP 742) to provide perfect type narrowing and static verification in tools like Basedpyright, Mypy and Ty.
- Zero-Escape Safety: Isolate crashing operations (panics) within the
.unsafenamespace to ensure that 'unwrapping' is always an intentional, visible choice. - Pragmatic Interop: Provide 'lifting' tools like
@catchto seamlessly convert standard Python exception-throwing code into functional containers.
Limitations & Constraints
- Type Invariance: Python's type system can be rigid regarding variance
in Unions. Returning a specific
Err(ValueError)when a signature expectsResult[T, Exception]may occasionally require explicit type annotations. - Higher-Kinded Types: Python lacks native support for higher-kinded
types, making operations like
flatten()returnResult[Any, Any]to maintain runtime flexibility while sacrificing some static detail. - Ecosystem Inertia: External Python libraries will continue to raise exceptions. Lifting must be performed at the integration boundaries.
OkErr
module-attribute
Result
module-attribute
A type that represents either success (Ok) or failure (Err).
This is the foundational type for explicit error handling. Use is_ok() and
is_err() to narrow the type or match for exhaustive handling.
Examples:
Do
Do = Generator[Result[Any, E], Any, T]
A type alias for generator functions compatible with the @do decorator.
The generator yields Result variants to unwrap them and eventually returns
a value of type T.
DoAsync
A type alias for async generator functions compatible with the @do_async decorator.
Async generators must yield an Ok variant as their final step to simulate
a return value.
AssertOk
A context manager for asserting that Results must be Ok.
It automatically monitors local variable assignments within the block.
If any local variable is assigned an Err variant, it raises an
AssertionError immediately (fail-fast).
Note
The automatic scanning only works for the local scope where the
with assert_ok() block is defined.
Initialize the assert_ok context with a custom message.
Source code in src/result/result.py
__enter__
__enter__() -> AssertOk
Enter the assert_ok context and install the fail-fast tracer.
Source code in src/result/result.py
__exit__
check
check(result: Result[T, E]) -> T
Verify that a result is Ok within the context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
Result[T, E]
|
The Result to verify. |
required |
Returns:
| Type | Description |
|---|---|
T
|
The success value if Ok. |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If the result is an Err. |
Source code in src/result/result.py
CatchContext
A context manager for capturing exceptions and converting them to Results.
Usage
with catch(ValueError) as ctx: ... ctx.set(int("123")) ctx.result Ok(123)
Source code in src/result/result.py
Err
dataclass
A failed result containing an error state.
To handle the error safely, use pattern matching or recovery methods like
.or_else() or .unwrap_or().
Examples:
>>> res = Err("not found")
>>> match res:
... case Err(e):
... print(f"Error: {e}")
Error: not found
Initialize an Err variant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error
|
E_co
|
The error state or exception to wrap. |
required |
Source code in src/result/result.py
error
property
Access the error state.
Note: While accessible on the Err variant, it is recommended to use pattern matching or functional methods for maximum safety.
unsafe
property
__add__
__add__(other: Any) -> Result[Any, Any]
Support short-circuiting addition (+) operator.
Always returns self for Err variants, short-circuiting the addition.
Source code in src/result/result.py
__aiter__
async
__bool__
Truthiness check. Returns False for Err.
This enables idiomatic Python conditional narrowing:
if res: # type is narrowed to Ok
__getattr__
Educational runtime safeguard against common API mistakes.
Source code in src/result/result.py
__hash__
__iter__
__iter__() -> Generator[Result[Any, E_co], Any, Never]
and_then
and_then_async
async
cast_types
cast_types() -> Result[U, F]
Zero-runtime-cost type hint override for strict variance edge cases.
This allows manually guiding the type checker when it fails to infer complex union types correctly.
Returns:
| Type | Description |
|---|---|
Result[U, F]
|
The same instance, but with new type parameters for the checker. |
Source code in src/result/result.py
err
Convert to Optional[E]. Returns the error state.
Returns:
| Type | Description |
|---|---|
E_co
|
The contained error state. |
Examples:
expect
expect_err
filter
filter(
_predicate: Callable[[Any], bool], _error: object
) -> Err[E_co]
flatten
flatten() -> Err[E_co]
Short-circuit the flattening and return self unchanged.
Returns:
| Type | Description |
|---|---|
Err[E_co]
|
The current instance unchanged. |
inspect
inspect_async
inspect_err
is_err
Check if the result is an Err variant.
Returns:
| Type | Description |
|---|---|
Literal[True]
|
Always True for Err instances. |
is_err_and
Check if the result is an Err variant and matches a predicate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predicate
|
Callable[[E_co], bool]
|
A function to test the error state. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the result is Err and the predicate returns True. |
Examples:
Source code in src/result/result.py
is_ok
Check if the result is an Ok variant.
Returns:
| Type | Description |
|---|---|
Literal[False]
|
Always False for Err instances. |
is_ok_and
Check if the result is an Ok variant and matches a predicate.
Always returns False for Err instances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_predicate
|
Callable[[Any], bool]
|
Ignored for Err instances. |
required |
Returns:
| Type | Description |
|---|---|
Literal[False]
|
Always False. |
Source code in src/result/result.py
map
map(_func: Callable[[T_local], U]) -> Err[E_co]
map_async
async
map_async(
_func: Callable[[T_local], Awaitable[U]],
) -> Err[E_co]
Ignore success mapping and return self unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_func
|
Callable[[T_local], Awaitable[U]]
|
Ignored async mapping function. |
required |
Returns:
| Type | Description |
|---|---|
Err[E_co]
|
The current instance unchanged. |
Source code in src/result/result.py
map_err
map_err(func: Callable[[E_co], F]) -> Err[F]
Apply a function to the contained error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E_co], F]
|
A function to transform the error state. |
required |
Returns:
| Type | Description |
|---|---|
Err[F]
|
A new |
Examples:
Source code in src/result/result.py
map_exc
map_exc(mapping: Mapping[type[Exception], Any]) -> Err[Any]
Replace the error payload if its type exists in the mapping.
This is ideal for converting raw Python exceptions into domain-specific Enums or error codes immediately after they are caught.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mapping
|
Mapping[type[Exception], Any]
|
A dictionary mapping exception types to new values. |
required |
Returns:
| Type | Description |
|---|---|
Err[Any]
|
A new Err with the mapped value if a match was found, otherwise self. |
Examples:
>>> # Single mapping to Enum
>>> Err(ValueError("fail")).map_exc({ValueError: ErrorCode.INVALID})
Err(<ErrorCode.INVALID: 'invalid'>)
>>> # Multiple mappings
>>> mapping = {ValueError: ErrorCode.INVALID, KeyError: ErrorCode.MISSING}
>>> Err(KeyError("key")).map_exc(mapping)
Err(<ErrorCode.MISSING: 'missing'>)
Source code in src/result/result.py
map_or
Ignore the mapping and return the default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default
|
U
|
The fallback value. |
required |
_func
|
object
|
Ignored mapping function. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The default value. |
Examples:
Source code in src/result/result.py
map_or_else
Ignore the mapping and compute the default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default_func
|
Callable[[], U]
|
Function to generate the fallback value. |
required |
_func
|
object
|
Ignored mapping function. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The computed default value. |
Examples:
Source code in src/result/result.py
match
Exhaustively handle both success and failure cases.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
on_ok
|
Callable[[T_local], U]
|
Ignored success handler. |
required |
on_err
|
Callable[[E_co], U]
|
Function to call with the error state. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The result of the error handler. |
Examples:
Source code in src/result/result.py
ok
Convert to Optional[T]. Always returns None for Err variants.
Examples:
or_else
Call a function with the error to attempt recovery.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E_co], Result[T_local, F_local]]
|
A function that takes the error and returns a new |
required |
Returns:
| Type | Description |
|---|---|
Result[T_local, F_local]
|
The |
Examples:
Source code in src/result/result.py
product
replace
replace(_value: object) -> Err[E_co]
replace_err
replace_err(error: F) -> Err[F]
Discard the contained error and replace it with a constant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error
|
F
|
The new error to wrap in Err. |
required |
Returns:
| Type | Description |
|---|---|
Err[F]
|
A new |
Examples:
Source code in src/result/result.py
tap
tap(_func: Callable[[Any], Any]) -> Err[E_co]
tap_async
async
tap_async(
_func: Callable[[T_local], Awaitable[Any]],
) -> Err[E_co]
Ignore async success side effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_func
|
Callable[[T_local], Awaitable[Any]]
|
Ignored async side effect function. |
required |
Returns:
| Type | Description |
|---|---|
Err[E_co]
|
The current instance unchanged. |
Source code in src/result/result.py
tap_err
tap_err(func: Callable[[E_co], Any]) -> Err[E_co]
Call a function with the contained error for side effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E_co], Any]
|
A function called with the error state. |
required |
Returns:
| Type | Description |
|---|---|
Err[E_co]
|
The current instance unchanged. |
Examples:
Source code in src/result/result.py
transpose
transpose() -> Err[E_co]
Transpose an Err variant.
Err(e) remains Err(e) (wrapped in the optional result).
Returns:
| Type | Description |
|---|---|
Err[E_co]
|
Self unchanged. |
unwrap
unwrap_err
unwrap_or
Return the provided default value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default
|
T_local
|
The fallback value. |
required |
Returns:
| Type | Description |
|---|---|
T_local
|
The |
Examples:
Source code in src/result/result.py
unwrap_or_else
Call a function with the error to produce a fallback value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E_co], T_local]
|
A function that generates a fallback value from the error. |
required |
Returns:
| Type | Description |
|---|---|
T_local
|
The result of |
Examples:
Source code in src/result/result.py
unwrap_or_raise
Root-level unwrap_or_raise is disabled. Use .unsafe.unwrap_or_raise() instead.
Ok
dataclass
A container representing a successful computation result.
To access the value safely, use pattern matching, functional chaining,
or the .ok() conversion method.
Examples:
Initialize an Ok variant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T_co
|
The successful result to wrap. |
required |
Source code in src/result/result.py
unsafe
property
value
property
Access the success value.
Note: While accessible on the Ok variant, it is recommended to use pattern matching or functional methods for maximum safety.
__add__
__add__(other: Any) -> Result[Any, Any]
Support short-circuiting addition (+) operator.
If both are Ok, returns Ok(a + b). If other is Err, returns other.
Source code in src/result/result.py
__aiter__
async
__bool__
Truthiness check. Returns True for Ok.
This enables idiomatic Python conditional narrowing:
if res: # type is narrowed to Ok
__getattr__
Educational runtime safeguard against common API mistakes.
Source code in src/result/result.py
__hash__
__iter__
and_then
Chain another result-returning operation (FlatMap).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], Result[U, E_local]]
|
A function that takes the value and returns a new |
required |
Returns:
| Type | Description |
|---|---|
Result[U, E_local]
|
The |
Examples:
>>> def validate(n: int) -> Result[int, str]:
... return Ok(n) if n > 0 else Err("too small")
>>> Ok(10).and_then(validate)
Ok(10)
Source code in src/result/result.py
and_then_async
async
Chain an async computation that might fail.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], Awaitable[Result[U, E_local]]]
|
An async function that takes the value and returns a new Result. |
required |
Returns:
| Type | Description |
|---|---|
Result[U, E_local]
|
The Result returned by func. |
Examples:
>>> async def check(n):
... return Ok(n) if n > 0 else Err("low")
>>> await Ok(10).and_then_async(check)
Ok(10)
Source code in src/result/result.py
cast_types
cast_types() -> Result[U, F]
Zero-runtime-cost type hint override for strict variance edge cases.
This allows manually guiding the type checker when it fails to infer complex union types correctly.
Returns:
| Type | Description |
|---|---|
Result[U, F]
|
The same instance, but with new type parameters for the checker. |
Source code in src/result/result.py
err
Convert to Optional[E].
Returns:
| Type | Description |
|---|---|
object | None
|
Always returns |
Examples:
expect
expect_err
filter
filter(
predicate: Callable[[T_co], bool], error: E_local
) -> Result[T_co, E_local]
Convert success to failure if a condition is not met.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predicate
|
Callable[[T_co], bool]
|
A function that returns True for valid values. |
required |
error
|
E_local
|
The error state to use if the predicate returns False. |
required |
Returns:
| Type | Description |
|---|---|
Result[T_co, E_local]
|
Self if predicate is True, otherwise |
Examples:
>>> Ok(10).filter(lambda x: x > 5, "error")
Ok(10)
>>> Ok(3).filter(lambda x: x > 5, "too small")
Err('too small')
Source code in src/result/result.py
flatten
flatten() -> Ok[T_co]
flatten() -> Result[Any, Any]
Flatten a nested Result.
If the contained value is itself a Result (Ok or Err), it is returned.
Otherwise, the current instance is returned unchanged.
Returns:
| Type | Description |
|---|---|
Result[Any, Any]
|
The inner Result or self. |
Examples:
Source code in src/result/result.py
inspect
inspect_async
inspect_err
is_err
Check if the result is an Err variant.
Returns:
| Type | Description |
|---|---|
Literal[False]
|
Always False for Ok instances. |
is_err_and
Check if the result is an Err variant and matches a predicate.
Always returns False for Ok instances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_predicate
|
Callable[[Any], bool]
|
Ignored for Ok instances. |
required |
Returns:
| Type | Description |
|---|---|
Literal[False]
|
Always False. |
Source code in src/result/result.py
is_ok
Check if the result is an Ok variant.
Returns:
| Type | Description |
|---|---|
Literal[True]
|
Always True for Ok instances. |
is_ok_and
Check if the result is an Ok variant and matches a predicate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predicate
|
Callable[[T_co], bool]
|
A function to test the success value. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the result is Ok and the predicate returns True. |
Examples:
Source code in src/result/result.py
map
map(func: Callable[[T_co], U]) -> Ok[U]
Apply a function to the contained value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], U]
|
A pure function to transform the success value. |
required |
Returns:
| Type | Description |
|---|---|
Ok[U]
|
A new |
Examples:
Source code in src/result/result.py
map_async
async
map_async(func: Callable[[T_co], Awaitable[U]]) -> Ok[U]
Apply an async function to the contained value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], Awaitable[U]]
|
An async function to transform the success value. |
required |
Returns:
| Type | Description |
|---|---|
Ok[U]
|
A new Ok containing the transformed value. |
Examples:
Source code in src/result/result.py
map_err
map_err(_func: Callable[[Any], Any]) -> Ok[T_co]
map_exc
map_exc(
_mapping: Mapping[type[Exception], Any],
) -> Ok[T_co]
Ignore the error mapping and return self unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_mapping
|
Mapping[type[Exception], Any]
|
A dictionary mapping exception types to new values. |
required |
Returns:
| Type | Description |
|---|---|
Ok[T_co]
|
The current instance unchanged. |
Examples:
Source code in src/result/result.py
map_or
Apply a function to the value or return a default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_default
|
U
|
The fallback value used if this were an Err. |
required |
func
|
Callable[[T_co], U]
|
A function to transform the success value. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The transformed value. |
Examples:
Source code in src/result/result.py
map_or_else
Apply a function to the value or compute a default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_default_func
|
Callable[[], U]
|
A function to generate a fallback value. |
required |
func
|
Callable[[T_co], U]
|
A function to transform the success value. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The transformed value. |
Examples:
Source code in src/result/result.py
match
Exhaustively handle both success and failure cases.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
on_ok
|
Callable[[T_co], U]
|
Function to call with the success value. |
required |
on_err
|
Callable[[Any], U]
|
Function to call with the error state. |
required |
Returns:
| Type | Description |
|---|---|
U
|
The result of whichever function was called. |
Examples:
Source code in src/result/result.py
ok
Convert to Optional[T].
Returns:
| Type | Description |
|---|---|
T_co
|
The contained value. Always succeeds on |
Examples:
or_else
or_else(_func: object) -> Ok[T_co]
Ignore the recovery function and return self unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_func
|
object
|
Ignored recovery function. |
required |
Returns:
| Type | Description |
|---|---|
Ok[T_co]
|
The current instance unchanged. |
Examples:
Source code in src/result/result.py
product
Combine two results into a result of a tuple (Zip).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Result[U, E2]
|
Another Result to zipping with. |
required |
Returns:
| Type | Description |
|---|---|
Result[tuple[T_co, U], E_co | E2]
|
Ok((val1, val2)) if both are Ok, otherwise the first Err. |
Examples:
Source code in src/result/result.py
replace
replace(value: U) -> Ok[U]
Discard the contained value and replace it with a constant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
U
|
The new value to wrap in Ok. |
required |
Examples:
replace_err
replace_err(_error: object) -> Ok[T_co]
tap
tap(func: Callable[[T_co], Any]) -> Ok[T_co]
Call a function with the contained value for side effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], Any]
|
A function called with the success value. |
required |
Returns:
| Type | Description |
|---|---|
Ok[T_co]
|
The current instance unchanged. |
Examples:
Source code in src/result/result.py
tap_async
async
tap_async(
func: Callable[[T_co], Awaitable[Any]],
) -> Ok[T_co]
Call an async function with the contained value for side effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T_co], Awaitable[Any]]
|
An async function called with the success value. |
required |
Returns:
| Type | Description |
|---|---|
Ok[T_co]
|
The current instance unchanged. |
Examples:
Source code in src/result/result.py
tap_err
tap_err(_func: Callable[[Any], Any]) -> Ok[T_co]
transpose
transpose() -> Ok[Any] | None
Transpose a Result of an Optional into an Optional of a Result.
Ok(None) becomes None. Ok(Some(v)) becomes Ok(v).
Returns:
| Type | Description |
|---|---|
Ok[Any] | None
|
None if the value is None, otherwise self. |
Examples:
Source code in src/result/result.py
unwrap
unwrap_err
unwrap_or
Extract the contained value, ignoring the default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_default
|
object
|
Ignored fallback value. |
required |
Returns:
| Type | Description |
|---|---|
T_co
|
The contained success value. |
Examples:
Source code in src/result/result.py
unwrap_or_else
Extract the contained value, ignoring the fallback function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
_func
|
Callable[[Any], T_co]
|
Ignored fallback function. |
required |
Returns:
| Type | Description |
|---|---|
T_co
|
The contained success value. |
Examples:
Source code in src/result/result.py
unwrap_or_raise
Root-level unwrap_or_raise is disabled. Use .unsafe.unwrap_or_raise() instead.
UnwrapError
UnwrapError(result: Result[Any, Any], message: str)
Bases: RuntimeError
Exception raised when an unsafe unwrap operation fails.
Attributes:
| Name | Type | Description |
|---|---|---|
result |
The Result instance that failed to unwrap. |
Source code in src/result/result.py
any_ok
Return the first Ok variant found, or a list of all errors if none succeeded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
Iterable[Result[T_local, E_local]]
|
An iterable of Results. |
required |
Returns:
| Type | Description |
|---|---|
Result[T_local, list[E_local]]
|
The first Ok found, or Err containing a list of all encountered errors. |
Examples:
Source code in src/result/result.py
as_err
as_err(
exception: E_in,
mapping: type[Exception]
| tuple[type[Exception], ...]
| Mapping[type[Exception], E_out]
| None = None,
*,
map_to: E_out | None = None,
) -> Err[E_in | E_out]
Lift a caught exception into an Err variant with optional mapping.
This pinpoint utility is ideal for manual conversion inside standard try/except blocks. It uses the same mapping logic as the @catch decorator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
E_in
|
The exception instance to wrap. |
required |
mapping
|
type[Exception] | tuple[type[Exception], ...] | Mapping[type[Exception], E_out] | None
|
Optional exception type, tuple, or dict for transformation. |
None
|
map_to
|
E_out | None
|
Optional value to use as the error if mapping is a type/tuple. |
None
|
Returns:
| Type | Description |
|---|---|
Err[E_in | E_out]
|
An Err variant containing the (potentially mapped) error. |
Examples:
>>> try:
... raise ValueError("fail")
... except Exception as e:
... res = as_err(e, {ValueError: "invalid"})
>>> res
Err('invalid')
>>> # Using map_to
>>> try:
... raise ValueError("fail")
... except Exception as e:
... res = as_err(e, ValueError, map_to="mapped")
>>> res
Err('mapped')
Source code in src/result/result.py
assert_ok
assert_ok(result_or_message: Result[T, E]) -> T
assert_ok(
result_or_message: str = "Result was Err",
) -> AssertOk
A dual-purpose utility for asserting that a Result must be Ok.
Can be used as a standalone function or as a context manager. If a Result is an Err, it raises an AssertionError.
Functional Mode
When passed a Result, it returns the success value or raises
AssertionError immediately. This is the high-performance way to
assert invariants.
Context Manager Mode
When used as a context manager, it automatically monitors local variable
assignments within the block using sys.settrace. If any local variable
is assigned an Err variant, it raises an AssertionError (fail-fast).
Performance & Behavior Notes:
- Overhead: The context manager installs a trace function, which
introduces performance overhead compared to functional
usage. Use it for scripts and prototypes rather than hot loops.
- Scanning Scope: The automatic scanning only catches Err variants
that are assigned to a variable name in the local scope.
Unassigned return values will NOT be caught.
Examples:
>>> # 1. Functional usage (Low overhead)
>>> val = assert_ok(Ok(10))
>>> # assert_ok(Err("fail")) # Raises AssertionError
>>> # 2. Automatic context manager usage (Higher overhead, Fail-fast)
>>> with assert_ok("Critical operations"):
... res = Ok(1) # Fine
... # res2 = Err("boom") # Raises AssertionError immediately
>>> # 3. Explicit check usage (Lower overhead in context)
>>> with assert_ok() as ctx:
... val = ctx.check(Ok(42))
Source code in src/result/result.py
catch
catch(
exceptions: type[E],
func: Callable[P, T],
*,
map_to: Any = None,
) -> Callable[P, Result[T, Any]]
catch(
exceptions: type[E],
func: Callable[P, Coroutine[Any, Any, T]],
*,
map_to: Any = None,
) -> Callable[P, Awaitable[Result[T, Any]]]
catch(
exceptions: type[E],
func: None = None,
*,
map_to: Any = None,
) -> _CatchDecoratorOrContext[E]
catch(
exceptions: Mapping[type[Exception], Any],
func: Callable[P, T],
) -> Callable[P, Result[T, Any]]
catch(
exceptions: Mapping[type[Exception], Any],
func: Callable[P, Coroutine[Any, Any, T]],
*,
map_to: Any = None,
) -> Callable[P, Awaitable[Result[T, Any]]]
catch(
exceptions: Mapping[type[Exception], Any],
func: None = None,
) -> _CatchDecoratorOrContext[Exception]
catch(
exceptions: tuple[type[E1], type[E2]],
func: Callable[P, T],
*,
map_to: Any = None,
) -> Callable[P, Result[T, Any]]
catch(
exceptions: tuple[type[E1], type[E2]],
func: Callable[P, Coroutine[Any, Any, T]],
*,
map_to: Any = None,
) -> Callable[P, Awaitable[Result[T, Any]]]
catch(
exceptions: tuple[type[E1], type[E2]],
func: None = None,
*,
map_to: Any = None,
) -> _CatchDecoratorOrContext[E1 | E2]
catch(
exceptions: tuple[type[Exception], ...],
func: Callable[P, T],
*,
map_to: Any = None,
) -> Callable[P, Result[T, Any]]
catch(
exceptions: tuple[type[Exception], ...],
func: Callable[P, Coroutine[Any, Any, T]],
*,
map_to: Any = None,
) -> Callable[P, Awaitable[Result[T, Any]]]
Execute a function and catch specified exceptions into a Result.
Can be used as a standalone wrapper or as a decorator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exceptions
|
Any
|
An exception type, tuple of types, or mapping of types to values. |
required |
func
|
Any
|
Optional function to wrap and execute immediately. |
None
|
map_to
|
Any
|
Optional value to return in Err if an exception matches. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A Result if |
Examples:
>>> # 1. Simple catch (returns the caught instance)
>>> @catch(ValueError)
... def parse(s: str) -> int:
... return int(s)
>>> parse("abc")
Err(ValueError("invalid literal for int()..."))
>>> # 2. Map single error to Enum using map_to
>>> @catch(ValueError, map_to=ErrorCode.INVALID)
... def parse_safe(s: str) -> int:
... return int(s)
>>> parse_safe("abc")
Err(<ErrorCode.INVALID: 'invalid'>)
>>> # 3. Map multiple errors to Enums using a dictionary
>>> error_map = {ValueError: ErrorCode.INVALID, KeyError: ErrorCode.MISSING}
>>> @catch(error_map)
... def risky_op(x):
... if x == 0:
... raise ValueError
... if x == 1:
... raise KeyError
... return "ok"
>>> risky_op(0)
Err(<ErrorCode.INVALID: 'invalid'>)
>>> risky_op(1)
Err(<ErrorCode.MISSING: 'missing'>)
Source code in src/result/result.py
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 | |
catch_call
catch_call(
exceptions: type[E],
func: Callable[..., T],
*args: Any,
map_to: Any = None,
**kwargs: Any,
) -> Result[T, Any]
catch_call(
exceptions: tuple[type[E1], type[E2]],
func: Callable[..., T],
*args: Any,
map_to: Any = None,
**kwargs: Any,
) -> Result[T, Any]
catch_call(
exceptions: Mapping[type[Exception], Any],
func: Callable[..., T],
*args: Any,
**kwargs: Any,
) -> Result[T, Any]
catch_call(
exceptions: tuple[type[Exception], ...],
func: Callable[..., T],
*args: Any,
map_to: Any = None,
**kwargs: Any,
) -> Result[T, Any]
catch_call(
exceptions: Any,
func: Any,
*args: Any,
mapping: Mapping[type[Exception], Any] | None = None,
map_to: Any = None,
**kwargs: Any,
) -> Any
Execute a function inline and catch specified exceptions into a Result.
This allows for clean, single-line expression evaluation without needing to define a new function or open a context manager block.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exceptions
|
Any
|
An exception type, tuple, or mapping to catch. |
required |
func
|
Any
|
The function to execute. |
required |
*args
|
Any
|
Positional arguments for the function. |
()
|
mapping
|
Mapping[type[Exception], Any] | None
|
Optional explicit mapping dictionary. |
None
|
map_to
|
Any
|
Optional value to use as the error if an exception matches. |
None
|
**kwargs
|
Any
|
Keyword arguments for the function. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Ok(value) if successful, Err(mapped_error) if a specified exception was raised. |
Examples:
>>> import json
>>> # 1. Simple catch
>>> catch_call(json.JSONDecodeError, json.loads, '{"key": "value"}')
Ok({'key': 'value'})
>>> # 2. Catch with mapping
>>> catch_call(json.JSONDecodeError, json.loads, "invalid", map_to="bad_json")
Err('bad_json')
Source code in src/result/result.py
combine
Combine an iterable of results into a single result (All-or-Nothing).
If all results are Ok, returns an Ok containing a list of all values.
If any result is an Err, returns the first Err encountered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
Iterable[Result[T_local, E_local]]
|
An iterable of |
required |
Returns:
| Type | Description |
|---|---|
Result[list[T_local], E_local]
|
A combined |
Examples:
Source code in src/result/result.py
do
Helper for inline synchronous do-notation (generator expressions).
If an 'Err' is encountered, it short-circuits and returns that error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gen
|
Generator[Result[T_co, E_co], Any, T_co]
|
A generator expression yielding Results. |
required |
Returns:
| Type | Description |
|---|---|
Result[T_co, E_co]
|
The final Result of the chain. |
Examples:
Source code in src/result/result.py
do_async
async
Helper for inline asynchronous do-notation (generator expressions).
If an 'Err' is encountered, it short-circuits and returns that error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gen
|
AsyncGenerator[Result[T_co, E_co], Any]
|
An async generator expression yielding Results. |
required |
Returns:
| Type | Description |
|---|---|
Result[T_co, E_co]
|
The final Result of the chain. |
Examples:
>>> async def async_gen():
... yield Ok(1)
>>> await do_async(Ok(x + 2) async for x in async_gen())
Ok(3)
Source code in src/result/result.py
do_notation
do_notation(
arg: Callable[P, Do[T_local, E_local]],
*,
catch: None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Callable[P, Result[T_local, E_local]]
do_notation(
arg: type[Exception]
| tuple[type[Exception], ...]
| None = None,
*,
catch: type[Exception]
| tuple[type[Exception], ...]
| None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Callable[
[Callable[P, Do[T_local, E_local]]],
Callable[P, Result[T_local, E_local | Exception]],
]
do_notation(
arg: Callable[P, Do[T_local, E_local]]
| type[Exception]
| tuple[type[Exception], ...]
| None = None,
*,
catch: type[Exception]
| tuple[type[Exception], ...]
| None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Any
Enable imperative-style 'do-notation' for synchronous Result blocks.
This decorator allows writing code that looks procedural by using yield
to unwrap Result values. If any yield receives an Err, the function
short-circuits immediately and returns that Err.
Notes
The final 'return' value is automatically wrapped in an Ok variant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arg
|
Callable[P, Do[T_local, E_local]] | type[Exception] | tuple[type[Exception], ...] | None
|
The function to decorate, or an exception type to catch. |
None
|
catch
|
type[Exception] | tuple[type[Exception], ...] | None
|
Optional keyword-only exception type to catch and lift into Err. |
None
|
remap
|
dict[type[Any], type[Any]] | None
|
Optional mapping of internal error types to high-level domain errors. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The decorated function or a decorator factory. |
Examples:
>>> @do_notation
... def process(x):
... a = yield Ok(x + 1)
... b = yield Ok(a * 2)
... return b
>>> process(5)
Ok(12)
>>> @do_notation(catch=ValueError)
... def risky(s):
... val = yield Ok(int(s))
... return val
>>> risky("not a number")
Err(ValueError(...))
Source code in src/result/result.py
do_notation_async
do_notation_async(
arg: Callable[P, DoAsync[T_local, E_local]],
*,
catch: None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Callable[
P, Coroutine[Any, Any, Result[T_local, E_local]]
]
do_notation_async(
arg: type[Exception]
| tuple[type[Exception], ...]
| None = None,
*,
catch: type[Exception]
| tuple[type[Exception], ...]
| None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Callable[
[Callable[P, DoAsync[T_local, E_local]]],
Callable[
P,
Coroutine[
Any, Any, Result[T_local, E_local | Exception]
],
],
]
do_notation_async(
arg: Callable[P, DoAsync[T_local, E_local]]
| type[Exception]
| tuple[type[Exception], ...]
| None = None,
*,
catch: type[Exception]
| tuple[type[Exception], ...]
| None = None,
remap: dict[type[Any], type[Any]] | None = None,
) -> Any
Enable imperative-style 'do-notation' for asynchronous Result blocks.
This is the async version of @do_notation. It supports yield await for
asynchronous Result-returning functions.
Notes
Unlike the sync version, the final result MUST be yielded as an Ok variant because async generators do not support native return values in this context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arg
|
Callable[P, DoAsync[T_local, E_local]] | type[Exception] | tuple[type[Exception], ...] | None
|
The async function to decorate, or an exception type to catch. |
None
|
catch
|
type[Exception] | tuple[type[Exception], ...] | None
|
Optional keyword-only exception type to catch and lift into Err. |
None
|
remap
|
dict[type[Any], type[Any]] | None
|
Optional mapping of internal error types to high-level domain errors. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
The decorated async function or a decorator factory. |
Examples:
>>> @do_notation_async
... async def get_data(user_id):
... user = yield await fetch_user(user_id) # fetch_user returns Result
... data = yield await fetch_posts(user["id"])
... yield Ok(data)
>>> await get_data(1)
Ok([...])
Source code in src/result/result.py
from_optional
from_optional(
value: T_co | None, error: E_co
) -> Result[T_co, E_co]
Create a Result from an optional value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
T_co | None
|
The optional value to wrap. |
required |
error
|
E_co
|
The error to return if the value is None. |
required |
Returns:
| Type | Description |
|---|---|
Result[T_co, E_co]
|
Ok(value) if value is not None, otherwise Err(error). |
Examples:
Source code in src/result/result.py
is_err
Check if a result is an Err variant and narrow its type.
Examples:
>>> res: Result[int, str] = Err("fail")
>>> if is_err(res):
... # res is now typed as Err[str]
... print(res._error)
Source code in src/result/result.py
is_ok
Check if a result is an Ok variant and narrow its type.
Examples:
>>> res: Result[int, str] = Ok(10)
>>> if is_ok(res):
... # res is now typed as Ok[int]
... print(res._value)
Source code in src/result/result.py
map2
map2(
res1: Result[T1, E1],
res2: Result[T2, E2],
func: Callable[[T1, T2], U],
) -> Result[U, E1 | E2]
Apply a binary function to the values of two results (Zipping Map).
If both are Ok, returns Ok(func(val1, val2)). If either is Err, returns the first Err encountered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
res1
|
Result[T1, E1]
|
First Result. |
required |
res2
|
Result[T2, E2]
|
Second Result. |
required |
func
|
Callable[[T1, T2], U]
|
A function taking two success values. |
required |
Returns:
| Type | Description |
|---|---|
Result[U, E1 | E2]
|
A new Result. |
Examples:
Source code in src/result/result.py
partition
partition(
results: Iterable[Result[T_local, E_local]],
) -> tuple[list[T_local], list[E_local]]
Partition an iterable of results into two lists.
Unlike combine, this does not short-circuit. It collects all values
into two separate lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
Iterable[Result[T_local, E_local]]
|
An iterable of |
required |
Returns:
| Type | Description |
|---|---|
tuple[list[T_local], list[E_local]]
|
A tuple of (ok_values, err_states). |
Examples:
Source code in src/result/result.py
retry_result
retry_result(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[E], bool],
catch: None = None,
) -> Callable[
[Callable[P, Result[T, E]]],
Callable[P, Result[T, E]],
]
retry_result(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: None = None,
catch: None = None,
) -> Callable[
[Callable[P, Result[T, E]]],
Callable[P, Result[T, E]],
]
retry_result(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[E_exc], bool] | None = None,
catch: type[E_exc],
) -> Callable[
[Callable[P, T]], Callable[P, Result[T, E_exc]]
]
retry_result(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[Any], bool] | None = None,
catch: type[Exception]
| tuple[type[Exception], ...]
| Mapping[type[Exception], Any]
| None = None,
) -> Any
A resilience decorator for synchronous functions that return a Result.
It will automatically re-execute the function if it returns an Err variant,
up to the specified number of attempts.
If catch is provided, it will also catch specified exceptions and turn
them into Err variants before deciding whether to retry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attempts
|
int
|
Total number of attempts to try (default 3). |
3
|
delay
|
float
|
Initial delay in seconds between retries (default 0). |
0
|
backoff
|
float
|
Multiplier for the delay after each attempt (default 1.0). |
1.0
|
jitter
|
bool | float
|
If True (or a float), add randomness to the delay. |
False
|
retry_if
|
Callable[[Any], bool] | None
|
Optional predicate to decide if an error should be retried. |
None
|
catch
|
type[Exception] | tuple[type[Exception], ...] | Mapping[type[Exception], Any] | None
|
Optional exception types to catch and lift into Results. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A decorator that adds retry logic to the function. |
Notes & Footguns:
- Idempotency: Retrying functions with side effects (e.g., DB writes)
can be dangerous if the operation is not idempotent.
- Exception Scoping: If catch is NOT used, and the function raises
an exception, the retry logic will NOT trigger (the exception will
bubble up). The retry logic only reacts to Err return values.
- Wait Times: High attempts and backoff values can lead to
extremely long execution times.
Examples:
>>> # 1. Basic Retry (Reacts to Err return)
>>> @retry_result(attempts=3)
... def unstable():
... return Err("fail")
>>> unstable()
Err('fail') # Tried 3 times
>>> # 2. Exponential Backoff with Jitter
>>> @retry_result(attempts=5, delay=0.1, backoff=2.0, jitter=True)
... def network_call():
... return Err("timeout")
>>> # 3. Conditional Retry (only retry on transient errors)
>>> @retry_result(attempts=3, retry_if=lambda e: e == "temporary")
... def db_op():
... return Err("permanent")
>>> db_op()
Err('permanent') # Fails fast, only tried once
>>> # 4. Internal Exception Catching (Lifting)
>>> @retry_result(attempts=3, catch=ValueError)
... def parse_stuff(s):
... return int(s) # Raises ValueError -> Err -> Retry
>>> parse_stuff("abc")
Err(ValueError("invalid literal for int()..."))
>>> # 5. Stacking with @catch
>>> @retry_result(attempts=2)
... @catch(KeyError)
... def get_config():
... raise KeyError("missing")
>>> get_config()
Err(KeyError('missing')) # @catch fires first, then @retry sees Err
Source code in src/result/result.py
2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 | |
retry_result_async
retry_result_async(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[E], bool],
catch: None = None,
) -> Callable[
[Callable[P, Awaitable[Result[T, E]]]],
Callable[P, Awaitable[Result[T, E]]],
]
retry_result_async(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: None = None,
catch: None = None,
) -> Callable[
[Callable[P, Awaitable[Result[T, E]]]],
Callable[P, Awaitable[Result[T, E]]],
]
retry_result_async(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[E_exc], bool] | None = None,
catch: type[E_exc],
) -> Callable[
[Callable[P, Awaitable[T]]],
Callable[P, Awaitable[Result[T, E_exc]]],
]
retry_result_async(
attempts: int = 3,
delay: float = 0,
backoff: float = 1.0,
*,
jitter: bool | float = False,
retry_if: Callable[[Any], bool] | None = None,
catch: type[Exception]
| tuple[type[Exception], ...]
| Mapping[type[Exception], Any]
| None = None,
) -> Any
A resilience decorator for asynchronous functions that return a Result.
This is the asynchronous version of retry_result.
It will automatically re-execute the function if it returns an Err variant,
up to the specified number of attempts.
If catch is provided, it will also catch specified exceptions and turn
them into Err variants before deciding whether to retry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attempts
|
int
|
Total number of attempts to try (default 3). |
3
|
delay
|
float
|
Initial delay in seconds between retries (default 0). |
0
|
backoff
|
float
|
Multiplier for the delay after each attempt (default 1.0). |
1.0
|
jitter
|
bool | float
|
If True (or a float), add randomness to the delay. |
False
|
retry_if
|
Callable[[Any], bool] | None
|
Optional predicate to decide if an error should be retried. |
None
|
catch
|
type[Exception] | tuple[type[Exception], ...] | Mapping[type[Exception], Any] | None
|
Optional exception types to catch and lift into Results. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A decorator that adds retry logic to the async function. |
Notes & Footguns:
- Idempotency: Retrying functions with side effects (e.g., POST calls)
can be dangerous if the operation is not idempotent.
- Exception Scoping: If catch is NOT used, and the function raises
an exception, the retry logic will NOT trigger (the exception will
bubble up).
- Concurrency: This decorator retries in series. For parallel
retries, consider other orchestration patterns.
Examples:
>>> # 1. Async Basic Retry
>>> @retry_result_async(attempts=3)
... async def unstable():
... return Err("fail")
>>> await unstable()
Err('fail')
>>> # 2. Async Backoff with Jitter
>>> @retry_result_async(attempts=5, delay=0.1, backoff=2.0, jitter=0.5)
... async def fetch_data():
... return Err("timeout")
>>> # 3. Async Conditional Retry
>>> @retry_result_async(attempts=3, retry_if=lambda e: e.status == 429)
... async def api_call():
... return Err(Response(status=500))
>>> await api_call()
Err(Response(status=500)) # Fails fast, not a 429
>>> # 4. Async Internal Exception Catching
>>> @retry_result_async(attempts=3, catch=asyncio.TimeoutError)
... async def timed_op():
... raise asyncio.TimeoutError
>>> await timed_op()
Err(asyncio.TimeoutError())
Source code in src/result/result.py
2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 | |