Skip to content

Commit 818c764

Browse files
astynaxsobolevn
authored andcommitted
add map_failure method (#91)
* add `map_failure` method * fix tests, fix linter * Update container.py
1 parent bba9029 commit 818c764

File tree

4 files changed

+44
-13
lines changed

4 files changed

+44
-13
lines changed

docs/pages/container.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,13 @@ Returning execution to the right track
140140
We also support two special methods to work with "failed"
141141
types like ``Failure``:
142142

143-
- :func:`.fix <returns.primitives.container.Fixable.fix>`
144-
is the opposite of ``map`` method
145-
that works only when container is in failed state
146143
- :func:`.rescue <returns.primitives.container.Rescueable.rescue>`
147144
is the opposite of ``bind`` method
148145
that works only when container is in failed state
146+
- :func:`.fix <returns.primitives.container.Fixable.fix>`
147+
transforms error to value (failure became success)
148+
that works only when container is in failed state,
149+
is the opposite of ``map`` method
149150

150151
``fix`` can be used to fix some fixable errors
151152
during the pipeline execution:
@@ -157,6 +158,9 @@ during the pipeline execution:
157158
def double(state: int) -> float:
158159
return state * 2.0
159160
161+
result: Result[Any, float] = Failure(1).map_error(double)
162+
# => Failure(2.0)
163+
160164
result: Result[float, int] = Failure(1).fix(double)
161165
# => Success(2.0)
162166

returns/primitives/container.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ class Fixable(Protocol[_ValueType, _ErrorType]):
9595
"""Represents containers that can be fixed and rescued."""
9696

9797
def fix(
98-
self, function: Callable[[_ErrorType], _NewErrorType],
99-
) -> 'Fixable[_ValueType, _NewErrorType]':
98+
self, function: Callable[[_ErrorType], _NewValueType],
99+
) -> 'Fixable[_NewValueType, _ErrorType]':
100100
"""
101-
Applies 'function' to the contents of the functor.
101+
Applies 'function' to the error and transforms failure to success.
102102
103103
And returns a new functor value.
104104
Works for containers that represent failure.
@@ -130,6 +130,18 @@ def rescue(
130130
Is the opposite of :meth:`~bind`.
131131
"""
132132

133+
def map_failure(
134+
self,
135+
function: Callable[[_ErrorType], _NewErrorType],
136+
) -> 'Fixable[_ValueType, _NewErrorType]':
137+
"""
138+
Uses 'function' to transform one error to another.
139+
140+
And returns a new functor value.
141+
Works for containers that represent failure.
142+
Is the opposite of :meth:`~map`.
143+
"""
144+
133145

134146
@runtime
135147
class Unwrapable(Protocol[_ValueType]):

returns/result.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def failure(self) -> _ErrorType: # pragma: no cover
8181
raise NotImplementedError()
8282

8383

84-
@final
84+
@final # noqa: Z214
8585
class _Failure(Result[Any, _ErrorType]):
8686
"""
8787
Represents a calculation which has failed.
@@ -100,6 +100,17 @@ def map(self, function): # noqa: A003
100100
"""Returns the '_Failure' instance that was used to call the method."""
101101
return self
102102

103+
def map_failure(self, function):
104+
"""
105+
Applies function to the error value.
106+
107+
Applies 'function' to the contents of the '_Failure' instance
108+
and returns a new '_Failure' object containing the result.
109+
'function' should accept a single "normal" (non-container) argument
110+
and return a non-container result.
111+
"""
112+
return _Failure(function(self._inner_value))
113+
103114
def bind(self, function):
104115
"""Returns the '_Failure' instance that was used to call the method."""
105116
return self
@@ -140,7 +151,7 @@ def failure(self):
140151
return self._inner_value
141152

142153

143-
@final
154+
@final # noqa: Z214
144155
class _Success(Result[_ValueType, Any]):
145156
"""
146157
Represents a calculation which has succeeded and contains the result.
@@ -166,6 +177,10 @@ def map(self, function): # noqa: A003
166177
"""
167178
return _Success(function(self._inner_value))
168179

180+
def map_failure(self, function):
181+
"""Returns the '_Success' instance that was used to call the method."""
182+
return self
183+
169184
def bind(self, function):
170185
"""
171186
Applies 'function' to the result of a previous calculation.

tests/test_result/test_result_map.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44

55

66
def test_map_success():
7-
"""Ensures that Failure identity works for Success container."""
7+
"""Ensures that Success is mappable."""
88
assert Success(5).map(str) == Success('5')
99

1010

1111
def test_map_failure():
12-
"""Ensures that Failure identity works for Success container."""
13-
assert Failure(5).map(str) == Failure(5)
12+
"""Ensures that Failure is mappable."""
13+
assert Failure(5).map_failure(str) == Failure('5')
1414

1515

1616
def test_fix_success():
17-
"""Ensures that Failure identity works for Success container."""
17+
"""Ensures that Success.fix is NoOp."""
1818
assert Success(5).fix(str) == Success(5)
1919

2020

2121
def test_fix_failure():
22-
"""Ensures that Failure identity works for Success container."""
22+
"""Ensures that Failure.fix produces the Success."""
2323
assert Failure(5).fix(str) == Success('5')

0 commit comments

Comments
 (0)