0
# Result
1
2
A Rust-inspired Result type for Python that enables robust error handling through explicit Ok/Err variants. The library provides type-safe value encapsulation with full type annotation support, allowing developers to replace traditional exception-based error handling with functional programming patterns.
3
4
## Package Information
5
6
- **Package Name**: result
7
- **Language**: Python
8
- **Installation**: `pip install result`
9
- **Python Requirements**: >=3.8
10
- **Type Support**: Full type annotations with `py.typed` marker
11
12
## Core Imports
13
14
```python
15
from result import Ok, Err, Result
16
```
17
18
For type checking and guards:
19
20
```python
21
from result import is_ok, is_err, OkErr
22
```
23
24
For functional utilities:
25
26
```python
27
from result import as_result, as_async_result, do, do_async
28
```
29
30
For exception handling:
31
32
```python
33
from result import UnwrapError
34
```
35
36
For version information:
37
38
```python
39
from result import __version__
40
print(__version__) # "0.17.0"
41
```
42
43
## Basic Usage
44
45
```python
46
from result import Ok, Err, Result, is_ok, is_err
47
48
def divide(a: int, b: int) -> Result[int, str]:
49
"""Divide two numbers, returning a Result type."""
50
if b == 0:
51
return Err("Cannot divide by zero")
52
return Ok(a // b)
53
54
# Using type guards for safe access
55
result = divide(10, 2)
56
if is_ok(result):
57
print(f"Success: {result.ok_value}") # Success: 5
58
elif is_err(result):
59
print(f"Error: {result.err_value}")
60
61
# Using isinstance for type checking
62
result = divide(10, 0)
63
if isinstance(result, Ok):
64
print(f"Success: {result.ok_value}")
65
elif isinstance(result, Err):
66
print(f"Error: {result.err_value}") # Error: Cannot divide by zero
67
68
# Python 3.10+ pattern matching
69
match divide(10, 3):
70
case Ok(value):
71
print(f"Result: {value}")
72
case Err(error):
73
print(f"Error: {error}")
74
```
75
76
## Architecture
77
78
The Result type implements a functional approach to error handling inspired by Rust's Result enum. Instead of relying on exceptions for control flow, it provides explicit success (`Ok`) and failure (`Err`) variants that must be handled consciously.
79
80
### Core Design Principles
81
82
- **Explicit Error Handling**: All potential failures are encoded in the type system, making error states visible and mandatory to handle
83
- **Type Safety**: Full generic type support ensures that both success values and error types are preserved through transformations
84
- **Immutability**: Result instances are immutable, preventing accidental state mutations
85
- **Composability**: Operations can be chained safely with short-circuiting behavior on errors
86
87
### Error Handling Philosophy
88
89
Traditional Python exception handling can lead to uncaught errors and unclear control flow:
90
91
```python
92
# Traditional approach - easy to miss error cases
93
def risky_operation():
94
if something_wrong():
95
raise ValueError("Something went wrong")
96
return success_value
97
98
try:
99
result = risky_operation()
100
# Easy to forget error handling
101
process(result)
102
except ValueError:
103
# Error handling often added as afterthought
104
handle_error()
105
```
106
107
The Result pattern makes error handling explicit and type-safe:
108
109
```python
110
# Result pattern - errors must be handled
111
def safe_operation() -> Result[SuccessType, ErrorType]:
112
if something_wrong():
113
return Err("Something went wrong")
114
return Ok(success_value)
115
116
result = safe_operation()
117
if is_ok(result):
118
process(result.ok_value) # Type-safe access
119
else:
120
handle_error(result.err_value) # Must handle error case
121
```
122
123
### Key Components
124
125
- **Result[T, E]**: Union type representing either success (`Ok[T]`) or failure (`Err[E]`)
126
- **Ok[T]**: Container for successful values with transformation methods
127
- **Err[E]**: Container for error values with recovery methods
128
- **Type Guards**: `is_ok()` and `is_err()` functions provide type-safe discrimination
129
- **Transformations**: `map()`, `and_then()`, `or_else()` enable safe operation chaining
130
- **Do Notation**: Generator-based syntax for complex operation sequences
131
132
This architecture enables building robust error-handling pipelines where failures are handled explicitly at each step, reducing bugs and improving code clarity.
133
134
## Capabilities
135
136
### Result Type Construction
137
138
Create Ok and Err instances to represent success and failure states.
139
140
```python { .api }
141
class Ok[T]:
142
def __init__(self, value: T) -> None:
143
"""Create an Ok instance containing a success value."""
144
145
class Err[E]:
146
def __init__(self, value: E) -> None:
147
"""Create an Err instance containing an error value."""
148
```
149
150
### Type Checking and Guards
151
152
Check Result types safely with type guards or isinstance.
153
154
```python { .api }
155
def is_ok(result: Result[T, E]) -> TypeGuard[Ok[T]]:
156
"""Type guard to check if a result is an Ok."""
157
158
def is_err(result: Result[T, E]) -> TypeGuard[Err[E]]:
159
"""Type guard to check if a result is an Err."""
160
```
161
162
Usage examples:
163
164
```python
165
result = Ok("success")
166
167
# Using type guard functions (recommended for type safety)
168
if is_ok(result):
169
# result is now typed as Ok[str]
170
value = result.ok_value
171
elif is_err(result):
172
# result is now typed as Err[E]
173
error = result.err_value
174
175
# Using isinstance checks
176
if isinstance(result, Ok):
177
value = result.ok_value
178
elif isinstance(result, Err):
179
error = result.err_value
180
181
# Using the OkErr convenience type
182
if isinstance(result, OkErr):
183
# result is either Ok or Err, but we don't know which
184
pass
185
```
186
187
### State Inspection Methods
188
189
Check the state of Result instances without type narrowing.
190
191
```python { .api }
192
# Ok methods
193
def is_ok(self) -> Literal[True]:
194
"""Return True for Ok instances."""
195
196
def is_err(self) -> Literal[False]:
197
"""Return False for Ok instances."""
198
199
# Err methods
200
def is_ok(self) -> Literal[False]:
201
"""Return False for Err instances."""
202
203
def is_err(self) -> Literal[True]:
204
"""Return True for Err instances."""
205
```
206
207
### Value Extraction
208
209
Extract success and error values from Result instances.
210
211
```python { .api }
212
# Ok value access
213
@property
214
def ok_value(self) -> T:
215
"""Access the success value (Ok only)."""
216
217
def ok(self) -> T:
218
"""Return the success value (Ok only)."""
219
220
def err(self) -> None:
221
"""Return None (Ok only)."""
222
223
# Err value access
224
@property
225
def err_value(self) -> E:
226
"""Access the error value (Err only)."""
227
228
def ok(self) -> None:
229
"""Return None (Err only)."""
230
231
def err(self) -> E:
232
"""Return the error value (Err only)."""
233
```
234
235
Usage examples:
236
237
```python
238
ok_result = Ok("success")
239
err_result = Err("failure")
240
241
# Direct property access (type-safe only after type checking)
242
if isinstance(ok_result, Ok):
243
value = ok_result.ok_value # "success"
244
245
if isinstance(err_result, Err):
246
error = err_result.err_value # "failure"
247
248
# Using ok() and err() methods
249
success_value = ok_result.ok() # "success"
250
error_value = err_result.err() # "failure"
251
```
252
253
### Object Protocol Methods
254
255
Standard Python object methods for comparison, hashing, and iteration.
256
257
```python { .api }
258
# Ok class methods
259
def __repr__(self) -> str:
260
"""Return string representation of Ok instance."""
261
262
def __eq__(self, other: Any) -> bool:
263
"""Check equality with another object."""
264
265
def __ne__(self, other: Any) -> bool:
266
"""Check inequality with another object."""
267
268
def __hash__(self) -> int:
269
"""Return hash value for Ok instance."""
270
271
def __iter__(self) -> Iterator[T]:
272
"""Allow iteration over Ok value (used in do notation)."""
273
274
# Err class methods
275
def __repr__(self) -> str:
276
"""Return string representation of Err instance."""
277
278
def __eq__(self, other: Any) -> bool:
279
"""Check equality with another object."""
280
281
def __ne__(self, other: Any) -> bool:
282
"""Check inequality with another object."""
283
284
def __hash__(self) -> int:
285
"""Return hash value for Err instance."""
286
287
def __iter__(self) -> Iterator[NoReturn]:
288
"""Special iterator that raises DoException (used in do notation)."""
289
```
290
291
Usage examples:
292
293
```python
294
ok1 = Ok("hello")
295
ok2 = Ok("hello")
296
ok3 = Ok("world")
297
298
print(repr(ok1)) # Ok('hello')
299
print(ok1 == ok2) # True
300
print(ok1 == ok3) # False
301
print(ok1 != ok3) # True
302
print(hash(ok1)) # Some hash value
303
304
err1 = Err("error")
305
err2 = Err("error")
306
print(repr(err1)) # Err('error')
307
print(err1 == err2) # True
308
309
# Iteration is used internally by do notation
310
for value in Ok("test"):
311
print(value) # "test"
312
```
313
314
### Deprecated Properties
315
316
Legacy properties maintained for backward compatibility.
317
318
```python { .api }
319
# Ok class
320
@property
321
def value(self) -> T:
322
"""
323
Return the inner value.
324
325
Warning:
326
Deprecated. Use `ok_value` or `err_value` instead.
327
This property will be removed in a future version.
328
"""
329
330
# Err class
331
@property
332
def value(self) -> E:
333
"""
334
Return the inner value.
335
336
Warning:
337
Deprecated. Use `ok_value` or `err_value` instead.
338
This property will be removed in a future version.
339
"""
340
```
341
342
Usage (not recommended):
343
344
```python
345
import warnings
346
347
ok_result = Ok("success")
348
with warnings.catch_warnings():
349
warnings.simplefilter("ignore", DeprecationWarning)
350
value = ok_result.value # "success" (but will show deprecation warning)
351
```
352
353
### Unwrap Operations
354
355
Extract values with various unwrap strategies and error handling.
356
357
```python { .api }
358
def unwrap(self) -> T:
359
"""
360
Return the value if Ok, raise UnwrapError if Err.
361
362
Raises:
363
UnwrapError: If called on an Err instance.
364
"""
365
366
def unwrap_err(self) -> E:
367
"""
368
Return the error if Err, raise UnwrapError if Ok.
369
370
Raises:
371
UnwrapError: If called on an Ok instance.
372
"""
373
374
def expect(self, message: str) -> T:
375
"""
376
Return the value if Ok, raise UnwrapError with custom message if Err.
377
378
Args:
379
message: Custom error message for UnwrapError.
380
381
Raises:
382
UnwrapError: If called on an Err instance.
383
"""
384
385
def expect_err(self, message: str) -> E:
386
"""
387
Return the error if Err, raise UnwrapError with custom message if Ok.
388
389
Args:
390
message: Custom error message for UnwrapError.
391
392
Raises:
393
UnwrapError: If called on an Ok instance.
394
"""
395
396
def unwrap_or(self, default: U) -> T | U:
397
"""
398
Return the value if Ok, return default if Err.
399
400
Args:
401
default: Default value to return for Err instances.
402
"""
403
404
def unwrap_or_else(self, op: Callable[[E], T]) -> T:
405
"""
406
Return the value if Ok, apply function to error if Err.
407
408
Args:
409
op: Function to apply to error value for Err instances.
410
"""
411
412
def unwrap_or_raise(self, e: Type[TBE]) -> T:
413
"""
414
Return the value if Ok, raise custom exception with error if Err.
415
416
Args:
417
e: Exception class to raise with the error value.
418
419
Raises:
420
e: Custom exception with error value as message.
421
"""
422
```
423
424
Usage examples:
425
426
```python
427
ok_result = Ok("success")
428
err_result = Err("failure")
429
430
# Basic unwrap (raises UnwrapError on failure)
431
try:
432
value = ok_result.unwrap() # "success"
433
value = err_result.unwrap() # raises UnwrapError
434
except UnwrapError as e:
435
print(f"Unwrap failed: {e}")
436
437
# Unwrap with custom message
438
try:
439
value = err_result.expect("Expected success") # raises UnwrapError
440
except UnwrapError as e:
441
print(f"Custom message: {e}")
442
443
# Unwrap with default value
444
value = ok_result.unwrap_or("default") # "success"
445
value = err_result.unwrap_or("default") # "default"
446
447
# Unwrap with function fallback
448
value = err_result.unwrap_or_else(lambda e: f"Error: {e}") # "Error: failure"
449
450
# Unwrap or raise custom exception
451
try:
452
value = err_result.unwrap_or_raise(ValueError) # raises ValueError("failure")
453
except ValueError as e:
454
print(f"Custom exception: {e}")
455
```
456
457
### Transformation Operations
458
459
Transform values and errors using map operations.
460
461
```python { .api }
462
def map(self, op: Callable[[T], U]) -> Result[U, E]:
463
"""
464
Transform the value if Ok, pass through if Err.
465
466
Args:
467
op: Function to transform the success value.
468
469
Returns:
470
Ok(op(value)) if Ok, unchanged Err if Err.
471
"""
472
473
async def map_async(self, op: Callable[[T], Awaitable[U]]) -> Result[U, E]:
474
"""
475
Async transform the value if Ok, pass through if Err.
476
477
Args:
478
op: Async function to transform the success value.
479
480
Returns:
481
Ok(await op(value)) if Ok, unchanged Err if Err.
482
"""
483
484
def map_or(self, default: U, op: Callable[[T], U]) -> U:
485
"""
486
Transform the value if Ok, return default if Err.
487
488
Args:
489
default: Default value to return for Err instances.
490
op: Function to transform the success value.
491
492
Returns:
493
op(value) if Ok, default if Err.
494
"""
495
496
def map_or_else(self, default_op: Callable[[], U], op: Callable[[T], U]) -> U:
497
"""
498
Transform the value if Ok, apply default function if Err.
499
500
Args:
501
default_op: Function to call for Err instances.
502
op: Function to transform the success value.
503
504
Returns:
505
op(value) if Ok, default_op() if Err.
506
"""
507
508
def map_err(self, op: Callable[[E], F]) -> Result[T, F]:
509
"""
510
Transform the error if Err, pass through if Ok.
511
512
Args:
513
op: Function to transform the error value.
514
515
Returns:
516
Unchanged Ok if Ok, Err(op(error)) if Err.
517
"""
518
```
519
520
Usage examples:
521
522
```python
523
ok_result = Ok(5)
524
err_result = Err("not a number")
525
526
# Transform success values
527
doubled = ok_result.map(lambda x: x * 2) # Ok(10)
528
failed = err_result.map(lambda x: x * 2) # Err("not a number")
529
530
# Transform with default value
531
result1 = ok_result.map_or(0, lambda x: x * 2) # 10
532
result2 = err_result.map_or(0, lambda x: x * 2) # 0
533
534
# Transform errors
535
better_err = err_result.map_err(lambda e: f"Error: {e}") # Err("Error: not a number")
536
```
537
538
### Chain Operations
539
540
Chain multiple Result-returning operations together.
541
542
```python { .api }
543
def and_then(self, op: Callable[[T], Result[U, E]]) -> Result[U, E]:
544
"""
545
Chain another Result-returning operation if Ok, pass through if Err.
546
547
Args:
548
op: Function that takes the success value and returns a Result.
549
550
Returns:
551
op(value) if Ok, unchanged Err if Err.
552
"""
553
554
async def and_then_async(self, op: Callable[[T], Awaitable[Result[U, E]]]) -> Result[U, E]:
555
"""
556
Async chain another Result-returning operation if Ok, pass through if Err.
557
558
Args:
559
op: Async function that takes the success value and returns a Result.
560
561
Returns:
562
await op(value) if Ok, unchanged Err if Err.
563
"""
564
565
def or_else(self, op: Callable[[E], Result[T, F]]) -> Result[T, F]:
566
"""
567
Chain alternative Result-returning operation if Err, pass through if Ok.
568
569
Args:
570
op: Function that takes the error value and returns a Result.
571
572
Returns:
573
Unchanged Ok if Ok, op(error) if Err.
574
"""
575
```
576
577
Usage examples:
578
579
```python
580
def parse_int(s: str) -> Result[int, str]:
581
try:
582
return Ok(int(s))
583
except ValueError:
584
return Err(f"'{s}' is not a valid integer")
585
586
def divide_by_two(n: int) -> Result[float, str]:
587
return Ok(n / 2.0)
588
589
# Chain operations
590
result = Ok("10").and_then(parse_int).and_then(divide_by_two) # Ok(5.0)
591
592
# Handle errors in chain
593
result = Err("failed").or_else(lambda e: Ok(f"Recovered from: {e}")) # Ok("Recovered from: failed")
594
```
595
596
### Inspection Operations
597
598
Inspect values without consuming the Result.
599
600
```python { .api }
601
def inspect(self, op: Callable[[T], Any]) -> Result[T, E]:
602
"""
603
Call function with the value if Ok, pass through unchanged.
604
605
Args:
606
op: Function to call with the success value (for side effects).
607
608
Returns:
609
The original Result unchanged.
610
"""
611
612
def inspect_err(self, op: Callable[[E], Any]) -> Result[T, E]:
613
"""
614
Call function with the error if Err, pass through unchanged.
615
616
Args:
617
op: Function to call with the error value (for side effects).
618
619
Returns:
620
The original Result unchanged.
621
"""
622
```
623
624
Usage examples:
625
626
```python
627
# Log success values without changing the Result
628
result = Ok("success").inspect(lambda x: print(f"Got value: {x}"))
629
# Prints: Got value: success
630
# result is still Ok("success")
631
632
# Log errors without changing the Result
633
result = Err("failed").inspect_err(lambda e: print(f"Error occurred: {e}"))
634
# Prints: Error occurred: failed
635
# result is still Err("failed")
636
```
637
638
### Function Decorators
639
640
Convert regular functions to return Result types.
641
642
```python { .api }
643
def as_result(*exceptions: Type[TBE]) -> Callable[[Callable[P, R]], Callable[P, Result[R, TBE]]]:
644
"""
645
Decorator to convert a function to return Result instead of raising exceptions.
646
647
Args:
648
*exceptions: Exception types to catch and convert to Err.
649
650
Returns:
651
Decorator function that wraps the original function.
652
653
Example:
654
@as_result(ValueError, TypeError)
655
def parse_int(s: str) -> int:
656
return int(s)
657
658
result = parse_int("123") # Ok(123)
659
result = parse_int("abc") # Err(ValueError(...))
660
"""
661
662
def as_async_result(*exceptions: Type[TBE]) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[Result[R, TBE]]]]:
663
"""
664
Decorator to convert an async function to return Result instead of raising exceptions.
665
666
Args:
667
*exceptions: Exception types to catch and convert to Err.
668
669
Returns:
670
Decorator function that wraps the original async function.
671
672
Example:
673
@as_async_result(ValueError, TypeError)
674
async def fetch_data(url: str) -> str:
675
# ... async code that might raise exceptions
676
return data
677
678
result = await fetch_data("http://example.com") # Ok(data) or Err(exception)
679
"""
680
```
681
682
Usage examples:
683
684
```python
685
@as_result(ValueError, TypeError)
686
def safe_int_conversion(value: str) -> int:
687
return int(value)
688
689
# Returns Ok(42) instead of int
690
result = safe_int_conversion("42")
691
692
# Returns Err(ValueError(...)) instead of raising
693
result = safe_int_conversion("not_a_number")
694
695
@as_async_result(ConnectionError, TimeoutError)
696
async def fetch_data(url: str) -> str:
697
# Some async operation that might fail
698
return "data"
699
700
# Usage
701
result = await fetch_data("http://example.com")
702
```
703
704
### Do Notation
705
706
Syntactic sugar for chaining Result operations using generator syntax.
707
708
```python { .api }
709
def do(gen: Generator[Result[T, E], None, None]) -> Result[T, E]:
710
"""
711
Do notation for Result - syntactic sugar for sequence of and_then() calls.
712
713
Args:
714
gen: Generator expression using Result values.
715
716
Returns:
717
The final Result from the generator.
718
719
Note:
720
Type annotation on the result is recommended for proper type inference.
721
"""
722
723
async def do_async(gen: Union[Generator[Result[T, E], None, None], AsyncGenerator[Result[T, E], None]]) -> Result[T, E]:
724
"""
725
Async version of do notation supporting both sync and async generators.
726
727
Args:
728
gen: Generator or async generator expression using Result values.
729
730
Returns:
731
The final Result from the generator.
732
"""
733
```
734
735
Usage examples:
736
737
```python
738
# Basic do notation
739
result: Result[int, str] = do(
740
Ok(x + y + z)
741
for x in Ok(1)
742
for y in Ok(2)
743
for z in Ok(3)
744
) # Ok(6)
745
746
# With error propagation - stops at first Err
747
result: Result[int, str] = do(
748
Ok(x + y)
749
for x in Ok(1)
750
for y in Err("failed") # This error propagates
751
) # Err("failed")
752
753
# Access previous values in later iterations
754
result: Result[str, str] = do(
755
Ok(f"{x}-{y}-{z}")
756
for x in Ok("a")
757
for y in Ok(f"{x}b") # Can use x from previous line
758
for z in Ok(f"{y}c") # Can use both x and y
759
) # Ok("a-ab-abc")
760
761
# Async do notation
762
async def get_async_result(x: int) -> Result[int, str]:
763
return Ok(x * 2)
764
765
result: Result[int, str] = await do_async(
766
Ok(x + y)
767
for x in await get_async_result(5) # Ok(10)
768
for y in Ok(3)
769
) # Ok(13)
770
```
771
772
### Exception Handling
773
774
Handle unwrap failures with custom exception types.
775
776
```python { .api }
777
class UnwrapError(Exception):
778
"""
779
Exception raised from unwrap and expect calls.
780
781
The original Result can be accessed via the .result attribute.
782
"""
783
784
def __init__(self, result: Result[object, object], message: str) -> None:
785
"""
786
Initialize UnwrapError with the original result and message.
787
788
Args:
789
result: The original Result that failed to unwrap.
790
message: Error message describing the failure.
791
"""
792
793
@property
794
def result(self) -> Result[Any, Any]:
795
"""Returns the original result that caused the unwrap failure."""
796
```
797
798
Usage examples:
799
800
```python
801
try:
802
value = Err("failed").unwrap()
803
except UnwrapError as e:
804
print(f"Unwrap failed: {e}")
805
original_result = e.result # Access the original Err("failed")
806
```
807
808
## Types
809
810
```python { .api }
811
# Type alias for Result
812
Result[T, E] = Union[Ok[T], Err[E]]
813
814
# Convenience constant for isinstance checks
815
OkErr: Final = (Ok, Err)
816
817
# Type variables
818
T = TypeVar("T", covariant=True) # Success type
819
E = TypeVar("E", covariant=True) # Error type
820
U = TypeVar("U") # Generic type variable
821
F = TypeVar("F") # Generic type variable
822
P = ParamSpec("P") # Function parameters
823
R = TypeVar("R") # Return type
824
TBE = TypeVar("TBE", bound=BaseException) # Exception type
825
826
# Package version
827
__version__: str = "0.17.0" # Package version string
828
```
829
830
## Pattern Matching (Python 3.10+)
831
832
The Result types support pattern matching with `__match_args__`:
833
834
```python
835
from result import Ok, Err
836
837
def handle_result(r: Result[int, str]) -> str:
838
match r:
839
case Ok(value):
840
return f"Success: {value}"
841
case Err(error):
842
return f"Error: {error}"
843
844
# Usage
845
print(handle_result(Ok(42))) # "Success: 42"
846
print(handle_result(Err("oops"))) # "Error: oops"
847
```
848
849
## Error Handling Best Practices
850
851
1. **Use type guards for safe access**: Always use `is_ok()`, `is_err()`, or `isinstance()` before accessing values.
852
853
2. **Prefer `unwrap_or()` over `unwrap()`**: Provide default values rather than risking exceptions.
854
855
3. **Chain operations with `and_then()`**: Build error-handling pipelines that short-circuit on failure.
856
857
4. **Use do notation for complex chains**: Simplify multiple sequential operations.
858
859
5. **Convert exceptions with decorators**: Use `@as_result()` to convert existing functions.
860
861
```python
862
# Good: Safe value access
863
if is_ok(result):
864
process_value(result.ok_value)
865
866
# Good: Default value handling
867
value = result.unwrap_or("default")
868
869
# Good: Operation chaining
870
final_result = (
871
initial_value
872
.and_then(validate)
873
.and_then(transform)
874
.map(format_output)
875
)
876
```