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

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

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

3

4

5 class 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__()