Coverage for src/monadc/try_/success.py: 100%
80 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-19 20:24 -0700
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-19 20:24 -0700
1from typing import Callable, Union, Any, Optional
2from .try_ import Try, T, U
5class Success(Try[T]):
6 """
7 Represents a successful computation in a Try monad.
9 Success contains the result of a computation that completed without throwing an exception.
10 """
12 __match_args__ = ("_value",)
14 def __new__(cls, value: T) -> 'Success[T]':
15 """Create a new Success instance directly, bypassing Try.__new__."""
16 return object.__new__(cls)
18 def __init__(self, value: T) -> None:
19 # If _value already exists, this is a second call from Python's object creation process
20 # We should ignore it since the object has already been properly initialized
21 if hasattr(self, '_value'):
22 return
24 self._value = value
26 def is_success(self) -> bool:
27 return True
29 def is_failure(self) -> bool:
30 return False
32 def get(self) -> T:
33 return self._value
35 def get_or_else(self, default: Union[T, Callable[[], T]]) -> T:
36 return self._value
38 def exception(self) -> Optional[Exception]:
39 return None
41 def map(self, func: Callable[[T], U]) -> Try[U]:
42 try:
43 result = func(self._value)
44 return Success(result)
45 except Exception as e:
46 from .failure import Failure
47 return Failure(e)
49 def flat_map(self, func: Callable[[T], Try[U]]) -> Try[U]:
50 try:
51 return func(self._value)
52 except Exception as e:
53 from .failure import Failure
54 return Failure(e)
56 def filter(self, predicate: Callable[[T], bool]) -> Try[T]:
57 try:
58 if predicate(self._value):
59 return self
60 else:
61 from .failure import Failure
62 return Failure(ValueError("Predicate does not hold"))
63 except Exception as e:
64 from .failure import Failure
65 return Failure(e)
67 def recover(self, func: Callable[[Exception], T]) -> Try[T]:
68 # Success doesn't need recovery
69 return self
71 def recover_with(self, func: Callable[[Exception], Try[T]]) -> Try[T]:
72 # Success doesn't need recovery
73 return self
75 def fold(self, if_failure: Callable[[Exception], U], if_success: Callable[[T], U]) -> U:
76 return if_success(self._value)
78 def transform(self, success_func: Callable[[T], Try[U]],
79 failure_func: Callable[[Exception], Try[U]]) -> Try[U]:
80 try:
81 return success_func(self._value)
82 except Exception as e:
83 from .failure import Failure
84 return Failure(e)
86 def foreach(self, func: Callable[[T], Any]) -> None:
87 func(self._value)
89 def or_else(self, alternative: Union[Try[T], Callable[[], Try[T]]]) -> Try[T]:
90 """Return self since Success doesn't need alternative."""
91 return self
93 def flatten(self) -> Any: # Returns Try[U] where T = Try[U]
94 """Flatten Success[Try[U]] to Try[U]. Raises TypeError if T is not Try[U]."""
95 if isinstance(self._value, Try):
96 return self._value
97 else:
98 # Following Scala's behavior: throw if trying to flatten non-nested Try
99 raise TypeError(f"Cannot flatten Success[{type(self._value).__name__}] - value must be a Try instance")
101 def flatten_safe(self) -> Any: # Returns Try[U]
102 """Safe flatten that returns self if not nested (idempotent behavior)."""
103 if isinstance(self._value, Try):
104 return self._value
105 else:
106 return self
108 def to_option(self) -> Any: # Returns Option[T]
109 from ..option import Option
110 return Option(self._value)
112 def to_either(self) -> Any: # Returns Either[Exception, T]
113 from ..either import Right
114 return Right(self._value)
116 def __eq__(self, other: object) -> bool:
117 if isinstance(other, Success):
118 return self._value == other._value # type: ignore[no-any-return]
119 return False
121 def __repr__(self) -> str:
122 return f"Success({self._value!r})"
124 def __str__(self) -> str:
125 return self.__repr__()