A library designed to implement the "Errors as Values" pattern in Python 3.14+. It bridges the gap between pure functional safety and the pragmatic realities of the exception-heavy Python ecosystem.
# Package name is 'result-pattern', but you import 'result'
$ pip install result-patternSee documentation for more details.
A Result is a Sum Type representing a state of being either completely successful (Ok) or completely failed (Err).
from result import Ok, Err, Result, is_ok
def divide(a: int, b: int) -> Result[float, str]:
if b == 0:
return Err("Cannot divide by zero")
return Ok(a / b)
res = divide(10, 2)
if is_ok(res):
# res is narrowed to Ok[float]
print(f"Success: {res.ok()}")Leverage Python 3.10+ native matching with optimized __match_args__:
match divide(10, 0):
case Ok(val):
print(f"Value: {val}")
case Err(msg):
print(f"Error: {msg}")Avoid nested if statements with linear pipelines:
Ok(10).map(lambda x: x * 2).tap(print).and_then(lambda x: Ok(x + 1))Bridge the procedural world of exceptions to the functional world of Results.
Lift exception-throwing code into Result containers with zero boilerplate.
from result import catch
@catch(ValueError)
def parse_int(s: str) -> int:
return int(s)
parse_int("10") # Ok(10)
parse_int("abc") # Err(ValueError(...))Immediately sanitize wild exceptions into strictly typed Domain Enums.
from enum import StrEnum
from result import catch
class ErrorCode(StrEnum):
INVALID = "invalid_input"
# Single mapping
@catch(ValueError, map_to=ErrorCode.INVALID)
def risky_op(s: str): ...
# Multiple mapping dictionary
@catch({ValueError: ErrorCode.INVALID, KeyError: "missing"})
def complex_op(x): ...Execute standard library functions inline without opening context blocks.
from result import catch_call
import json
res = catch_call(json.JSONDecodeError, json.loads, '{"key": "value"}')Lasso an entire block of wild Python code and capture the result safely.
from result import catch
import json
with catch(json.JSONDecodeError) as safe_block:
data = json.loads(payload)
# Perform complex logic...
safe_block.set(data["nested"]["key"])
# result is narrowed to Result[T, JSONDecodeError]
print(safe_block.result)Write procedural-looking code that automatically handles short-circuiting logic.
from result import do_notation, Do
@do_notation
def compile_pipeline(source: str) -> Do[str, Exception]:
tokens = yield tokenize(source) # Returns list[Token] or short-circuits Err
ast = yield parse(tokens) # Returns AST or short-circuits Err
code = yield generate(ast)
return code # Automatically wrapped in Ok| Utility | Description |
|---|---|
validate() |
Applicative: Accumulates all errors instead of failing fast. |
traverse() |
Maps a fallible function over an iterable, failing fast. |
flow() |
A sequential pipeline macro for piped data transformations. |
succeeds() |
Filters a collection of Results, returning only the success values. |
partition_exceptions() |
Splits a mixed list of [Value, Exception] into [Ok, Err]. |
While Result is mathematically pure, real-world systems often require fault tolerance. Outcome[T, E] is a Product Type that holds both a partial success value and diagnostic baggage simultaneously.
- Native Unpacking: Inherits from
NamedTupleforval, err = do_work()syntax. - Fault Tolerance: Retain your AST or data payload even if warnings/errors occurred.
- Odin Style: Use
.to_result()inside a@do_notationblock to simulate Odin'sor_return.
from result import Outcome
def parse_with_diagnostics(source: str) -> Outcome[AST, list[str]]:
# build AST and accumulate errors...
return Outcome(ast, accumulated_errors)
# 1. Procedural Unpacking (Go/Odin style)
ast, errors = parse_with_diagnostics(src)
if errors:
print(f"Warnings: {errors}")
# 2. Accumulation
new_outcome = Outcome(node, "e1").push_err("e2").merge(other_outcome)
# 3. Transition to Strict (or_return)
@do_notation
def strict_flow():
# .to_result() halts execution if any errors exist
ast = yield parse_with_diagnostics(src).to_result()
return emit(ast)Panics are isolated in the .unsafe namespace. Direct .unwrap() access is disabled at the root level to encourage safe functional patterns.
res = Err("fail")
res.unsafe.unwrap() # Explicit panicFull support for inheritance subtyping (e.g., Ok[Dog] is assignable to Result[Animal, Any]).