Outcome
The fault-tolerant Product Type for accumulating diagnostics and partial states.
result.outcome
Outcome: Product Type Error Handling for Modern Python.
This module provides the Outcome type, a fault-tolerant product type that
holds both a success value and an optional error state simultaneously.
Outcome
Bases: NamedTuple
A fault-tolerant Product Type holding a value and an error state.
'error' can be a single Exception, a Collection of Exceptions, or None. Unlike Result, an Outcome always contains a value, even if errors occurred.
Attributes:
| Name | Type | Description |
|---|---|---|
value |
T
|
The success or partial success value. |
error |
E | Any | None
|
The error state or diagnostic baggage. |
unsafe
property
__add__
__add__(other: Any) -> Outcome[Any, Any]
Support addition (+) operator.
Combines values using + and accumulates any errors.
Source code in src/result/outcome.py
__bool__
and_then
The monadic bind for Outcomes.
Applies the function to the value and concatenates errors from both Outcomes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T], Outcome[U, E2]]
|
A function returning a new Outcome. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[U, E | E2]
|
A new Outcome with the transformed value and combined errors. |
Examples:
>>> out = Outcome(10, "e1")
>>> out.and_then(lambda x: Outcome(x + 1, "e2"))
Outcome(value=11, error=['e1', 'e2'])
Source code in src/result/outcome.py
cast_types
cast_types() -> Outcome[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 |
|---|---|
Outcome[U, F]
|
The same instance, but with new type parameters for the checker. |
Source code in src/result/outcome.py
has_error
Check if an error exists, intelligently handling empty collections.
Returns:
| Type | Description |
|---|---|
bool
|
True if error is not None and (if it's a collection) is not empty. |
Examples:
>>> Outcome(10, None).has_error()
False
>>> Outcome(10, ValueError()).has_error()
True
>>> Outcome(10, []).has_error()
False
Source code in src/result/outcome.py
map
map(func: Callable[[T], U_inner]) -> Outcome[U_inner, E]
Transform the success value while preserving the error state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[T], U_inner]
|
A function to transform the value. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[U_inner, E]
|
A new Outcome with the transformed value and the same error. |
Examples:
Source code in src/result/outcome.py
map_err
map_err(func: Callable[[E], F]) -> Outcome[T, F]
Transform the error payload.
If the error is a collection, it intelligently maps the function over every item in the collection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E], F]
|
A function to transform the error(s). |
required |
Returns:
| Type | Description |
|---|---|
Outcome[T, F]
|
A new Outcome with the transformed error state. |
Examples:
>>> # Mapping over accumulated errors
>>> out = Outcome(10, [ValueError("a"), KeyError("b")])
>>> out.map_err(type)
Outcome(value=10, error=[<class 'ValueError'>, <class 'KeyError'>])
Source code in src/result/outcome.py
map_exc
map_exc(
mapping: Mapping[type[Exception], Any],
) -> Outcome[T, Any]
Transform specific exception types in the error payload.
This is ideal for converting raw Python exceptions into domain-specific Enums or error codes within a fault-tolerant Outcome.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mapping
|
Mapping[type[Exception], Any]
|
A dictionary mapping exception types to new values. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[T, Any]
|
A new Outcome with the mapped error if a match was found, otherwise self. |
Examples:
>>> # Single error mapping
>>> Outcome(10, ValueError("fail")).map_exc({ValueError: ErrorCode.INVALID})
Outcome(value=10, error=<ErrorCode.INVALID: 'invalid'>)
>>> # Collection mapping (accumulated diagnostics)
>>> out = Outcome(10, [ValueError("a"), KeyError("b")])
>>> out.map_exc({ValueError: "err_val", KeyError: "err_key"})
Outcome(value=10, error=['err_val', 'err_key'])
Source code in src/result/outcome.py
merge
Combine two independent outcomes.
Zips the values into a tuple and concatenates all accumulated errors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Outcome[U, E2]
|
Another Outcome to merge with. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[tuple[T, U], E | E2]
|
A merged Outcome with tuple value and combined errors. |
Examples:
>>> out1 = Outcome(1, "e1")
>>> out2 = Outcome(2, "e2")
>>> out1.merge(out2)
Outcome(value=(1, 2), error=['e1', 'e2'])
Source code in src/result/outcome.py
or_else
Recovery hatch for Outcomes with errors.
If errors exist, they are passed to the function to attempt recovery.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E | Any | None], Outcome[T, F]]
|
A function taking the current error state and returning a new Outcome. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[T, F]
|
The current instance if clean, or the result of func if errors exist. |
Examples:
>>> out = Outcome(0, "fail")
>>> out.or_else(lambda _: Outcome(1, None))
Outcome(value=1, error=None)
Source code in src/result/outcome.py
push_err
push_err(new_error: E2) -> Outcome[T, E | E2]
Append a new diagnostic error without altering the value.
If the current error is a list, the new error is appended. If it's a single value, both are wrapped in a new list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
new_error
|
E2
|
The new error to accumulate. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[T, E | E2]
|
A new Outcome with the expanded error state. |
Examples:
>>> Outcome(10, None).push_err("err1")
Outcome(value=10, error='err1')
>>> Outcome(10, "err1").push_err("err2")
Outcome(value=10, error=['err1', 'err2'])
Source code in src/result/outcome.py
tap_err
tap_err(
func: Callable[[E | Any | None], Any],
) -> Outcome[T, E]
Execute a side-effect only if an error exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[[E | Any | None], Any]
|
A function called with the error state if it exists. |
required |
Returns:
| Type | Description |
|---|---|
Outcome[T, E]
|
The current Outcome unchanged. |
Examples:
Source code in src/result/outcome.py
to_ok
to_ok() -> Ok[T]
Discard any error state and return the value wrapped in Ok.
Returns:
| Type | Description |
|---|---|
Ok[T]
|
An Ok variant containing the value. |
to_result
to_result() -> Result[T, E]
Convert the Outcome into a strict Sum Type (Result).
Returns:
| Type | Description |
|---|---|
Result[T, E]
|
Err(error) if errors exist, otherwise Ok(value). |
Examples:
>>> Outcome(10, None).to_result()
Ok(10)
>>> Outcome(10, ValueError("fail")).to_result()
Err(ValueError('fail'))
Source code in src/result/outcome.py
as_outcome
as_outcome(
exception: E_in,
default: T,
mapping: type[Exception]
| tuple[type[Exception], ...]
| Mapping[type[Exception], E_out]
| None = None,
*,
map_to: E_out | None = None,
) -> Outcome[T, E_in | E_out]
Lift a caught exception into an Outcome variant with a default value.
This pinpoint utility allows manual conversion inside standard try/except blocks while preserving the fault-tolerant Outcome pattern.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
E_in
|
The exception instance to wrap. |
required |
default
|
T
|
The success value to carry in the Outcome. |
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 |
|---|---|
Outcome[T, E_in | E_out]
|
An Outcome containing the default value and the (mapped) error. |
Examples:
>>> try:
... raise ValueError("fail")
... except Exception as e:
... out = as_outcome(e, default=0, mapping={ValueError: "mapped"})
>>> out
Outcome(value=0, error='mapped')
Source code in src/result/outcome.py
catch_outcome
catch_outcome(
exceptions: type[E], default: T, *, map_to: Any = None
) -> Callable[
[Callable[P, T_ret]],
Callable[P, Outcome[T | T_ret, Any]],
]
catch_outcome(
exceptions: Mapping[type[Exception], Any], default: T
) -> Callable[
[Callable[P, T_ret]],
Callable[P, Outcome[T | T_ret, Any]],
]
catch_outcome(
exceptions: tuple[type[Exception], ...],
default: T,
*,
map_to: Any = None,
) -> Callable[
[Callable[P, T_ret]],
Callable[P, Outcome[T | T_ret, Any]],
]
Catch exceptions and return an Outcome with a fallback value.
Requires a 'default' value to populate the Outcome payload on crash.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exceptions
|
Any
|
An exception type, tuple of types, or mapping of types to values. |
required |
default
|
Any
|
The fallback value to use if an exception is caught. |
required |
map_to
|
Any
|
Optional value to use as the error if an exception matches. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A decorator that wraps a function to return an Outcome. |
Examples:
>>> # 1. Simple catch with default
>>> @catch_outcome(ValueError, default=0)
... def parse(s: str) -> int:
... return int(s)
>>> parse("abc")
Outcome(value=0, error=ValueError(...))
>>> # 2. Map single error using map_to
>>> @catch_outcome(ValueError, default=-1, map_to=ErrorCode.INVALID)
... def risky_parse(s: str) -> int:
... return int(s)
>>> risky_parse("abc")
Outcome(value=-1, error=<ErrorCode.INVALID: 'invalid'>)
>>> # 3. Map multiple errors using a dictionary
>>> error_map = {ValueError: ErrorCode.INVALID, KeyError: ErrorCode.MISSING}
>>> @catch_outcome(error_map, default=None)
... def complex_op(x):
... if x == 0:
... raise ValueError
... if x == 1:
... raise KeyError
... return "ok"
>>> complex_op(0)
Outcome(value=None, error=<ErrorCode.INVALID: 'invalid'>)