Coverage for src/monadc/result/err.py: 100%

75 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-19 20:24 -0700

1from typing import Callable, Union, Any, Optional, cast 

2from .result import Result, T, E, U 

3 

4 

5class Err(Result[T, E]): 

6 """ 

7 Represents a failed computation in a Result monad. 

8 

9 Err contains the error value, following Rust conventions. 

10 Err values pass through transformations unchanged, allowing error information 

11 to propagate through a chain of operations. 

12 """ 

13 

14 __match_args__ = ("_value",) 

15 

16 def __new__(cls, value: E) -> 'Err[T, E]': 

17 """Create a new Err instance directly, bypassing Result.__new__.""" 

18 return object.__new__(cls) 

19 

20 def __init__(self, value: Any = None, **kwargs: Any) -> None: 

21 # If _value already exists, this is a second call from Python's object creation process 

22 # We should ignore it since the object has already been properly initialized 

23 if hasattr(self, '_value'): 

24 return 

25 

26 # Handle factory construction: Result(err_value=x) calls Err.__init__(err_obj, err_value=x) 

27 if 'err_value' in kwargs: 

28 self._value = kwargs['err_value'] 

29 # Handle direct construction: Err(value) 

30 else: 

31 self._value = value 

32 

33 def is_ok(self) -> bool: 

34 return False 

35 

36 def is_err(self) -> bool: 

37 return True 

38 

39 def __bool__(self) -> bool: 

40 """Err is falsy (following Rust conventions).""" 

41 return False 

42 

43 def ok(self) -> Any: # Returns Nil() 

44 from ..option import Nil 

45 return Nil() 

46 

47 def err(self) -> Any: # Returns Some(error) 

48 from ..option import Some 

49 return Some(self._value) 

50 

51 def unwrap(self) -> T: 

52 raise ValueError(f"Called unwrap() on an Err value: {self._value}") 

53 

54 def unwrap_err(self) -> E: 

55 return self._value # type: ignore[no-any-return] 

56 

57 def unwrap_or(self, default: T) -> T: 

58 return default 

59 

60 def unwrap_or_else(self, func: Callable[[E], T]) -> T: 

61 return func(self._value) 

62 

63 def map(self, func: Callable[[T], U]) -> Result[U, E]: 

64 # Err passes through unchanged 

65 return cast(Result[U, E], self) 

66 

67 def map_err(self, func: Callable[[E], U]) -> Result[T, U]: 

68 result = func(self._value) 

69 return Err(result) 

70 

71 def and_then(self, func: Callable[[T], Result[U, E]]) -> Result[U, E]: 

72 # Err passes through unchanged 

73 return cast(Result[U, E], self) 

74 

75 def or_else(self, func: Callable[[E], Result[T, U]]) -> Result[T, U]: 

76 return func(self._value) 

77 

78 def map_or(self, default: U, func: Callable[[T], U]) -> U: 

79 # Return default for Err (ignore func) 

80 return default 

81 

82 def map_or_else(self, default_func: Callable[[E], U], func: Callable[[T], U]) -> U: 

83 # Apply default_func to Err value (ignore func) 

84 return default_func(self._value) 

85 

86 def flatten(self) -> 'Result[Any, E]': 

87 # Err values are unchanged by flatten 

88 return self # type: ignore[return-value] 

89 

90 def transpose(self) -> 'Any': # Returns Option[Result[T, E]] 

91 # Err values transpose to Some(Err(...)) 

92 from ..option import Some 

93 return Some(self) 

94 

95 def and_(self, other: Result[U, E]) -> Result[U, E]: 

96 return cast(Result[U, E], self) 

97 

98 def or_(self, other: Result[T, U]) -> Result[T, U]: 

99 return other 

100 

101 def inspect(self, func: Callable[[T], Any]) -> Result[T, E]: 

102 # Err has no ok value to inspect 

103 return self 

104 

105 def inspect_err(self, func: Callable[[E], Any]) -> Result[T, E]: 

106 func(self._value) 

107 return self 

108 

109 def to_option(self) -> Any: # Returns Option[T] 

110 from ..option import Nil 

111 return Nil() 

112 

113 def to_try(self) -> Any: # Returns Try[T] 

114 from ..try_ import Failure 

115 # Convert error value to Exception if it's not already one 

116 if isinstance(self._value, Exception): 

117 return Failure(self._value) 

118 else: 

119 # Wrap non-exception errors in a RuntimeError 

120 return Failure(RuntimeError(str(self._value))) 

121 

122 def __eq__(self, other: object) -> bool: 

123 if isinstance(other, Err): 

124 return self._value == other._value # type: ignore[no-any-return] 

125 return False 

126 

127 def __repr__(self) -> str: 

128 return f"Err({self._value!r})" 

129 

130 def __str__(self) -> str: 

131 return self.__repr__()