diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f07aa..4ddeb3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ Possible log types: ## [Unreleased] +- `[added]` Add `inspect()` and `inspect_err()` methods (#185) + ## [0.16.1] - 2024-02-29 - `[fixed]` PyPI not showing description (#176) diff --git a/docs/result.md b/docs/result.md index e3ee642..731c1f1 100644 --- a/docs/result.md +++ b/docs/result.md @@ -13,7 +13,7 @@ --- - + ## function `as_result` @@ -30,7 +30,7 @@ Regular return values are turned into ``Ok(return_value)``. Raised exceptions of --- - + ## function `as_async_result` @@ -45,7 +45,7 @@ Make a decorator to turn an async function into one that returns a ``Result``. R --- - + ## function `is_ok` @@ -68,7 +68,7 @@ elif is_err(r): --- - + ## function `is_err` @@ -91,7 +91,7 @@ elif is_err(r): --- - + ## function `do` @@ -128,7 +128,7 @@ NOTE: If you exclude the type annotation e.g. `Result[float, int]` your type che --- - + ## function `do_async` @@ -278,6 +278,30 @@ Raise an UnwrapError since this type is `Ok` --- + + +### method `inspect` + +```python +inspect(op: 'Callable[[T], None]') → Result[T, E] +``` + +Calls a function with the contained value if `Ok`. Returns the original result. + +--- + + + +### method `inspect_err` + +```python +inspect_err(op: 'Callable[[E], None]') → Result[T, E] +``` + +Calls a function with the contained value if `Err`. Returns the original result. + +--- + ### method `is_err` @@ -451,12 +475,12 @@ Return the value. --- - + ## class `DoException` This is used to signal to `do()` that the result is an `Err`, which short-circuits the generator and returns that Err. Using this exception for control flow in `do()` allows us to simulate `and_then()` in the Err case: namely, we don't call `op`, we just return `self` (the Err). - + ### method `__init__` @@ -474,12 +498,12 @@ __init__(err: 'Err[E]') → None --- - + ## class `Err` A value that signifies failure and which stores arbitrary data for the error. - + ### method `__init__` @@ -510,7 +534,7 @@ Return the inner value. --- - + ### method `and_then` @@ -522,7 +546,7 @@ The contained result is `Err`, so return `Err` with the original value --- - + ### method `and_then_async` @@ -534,7 +558,7 @@ The contained result is `Err`, so return `Err` with the original value --- - + ### method `err` @@ -546,7 +570,7 @@ Return the error. --- - + ### method `expect` @@ -558,7 +582,7 @@ Raises an `UnwrapError`. --- - + ### method `expect_err` @@ -570,7 +594,31 @@ Return the inner value --- - + + +### method `inspect` + +```python +inspect(op: 'Callable[[T], None]') → Result[T, E] +``` + +Calls a function with the contained value if `Ok`. Returns the original result. + +--- + + + +### method `inspect_err` + +```python +inspect_err(op: 'Callable[[E], None]') → Result[T, E] +``` + +Calls a function with the contained value if `Err`. Returns the original result. + +--- + + ### method `is_err` @@ -584,7 +632,7 @@ is_err() → Literal[True] --- - + ### method `is_ok` @@ -598,7 +646,7 @@ is_ok() → Literal[False] --- - + ### method `map` @@ -610,7 +658,7 @@ Return `Err` with the same value --- - + ### method `map_async` @@ -622,7 +670,7 @@ The contained result is `Ok`, so return the result of `op` with the original val --- - + ### method `map_err` @@ -634,7 +682,7 @@ The contained result is `Err`, so return `Err` with original error mapped to a n --- - + ### method `map_or` @@ -646,7 +694,7 @@ Return the default value --- - + ### method `map_or_else` @@ -658,7 +706,7 @@ Return the result of the default operation --- - + ### method `ok` @@ -670,7 +718,7 @@ Return `None`. --- - + ### method `or_else` @@ -682,7 +730,7 @@ The contained result is `Err`, so return the result of `op` with the original va --- - + ### method `unwrap` @@ -694,7 +742,7 @@ Raises an `UnwrapError`. --- - + ### method `unwrap_err` @@ -706,7 +754,7 @@ Return the inner value --- - + ### method `unwrap_or` @@ -718,7 +766,7 @@ Return `default`. --- - + ### method `unwrap_or_else` @@ -730,7 +778,7 @@ The contained result is ``Err``, so return the result of applying ``op`` to the --- - + ### method `unwrap_or_raise` @@ -743,14 +791,14 @@ The contained result is ``Err``, so raise the exception with the value. --- - + ## class `UnwrapError` Exception raised from ``.unwrap_<...>`` and ``.expect_<...>`` calls. The original ``Result`` can be accessed via the ``.result`` attribute, but this is not intended for regular use, as type information is lost: ``UnwrapError`` doesn't know about both ``T`` and ``E``, since it's raised from ``Ok()`` or ``Err()`` which only knows about either ``T`` or ``E``, not both. - + ### method `__init__` diff --git a/src/result/result.py b/src/result/result.py index 06b78b9..30bf9a8 100644 --- a/src/result/result.py +++ b/src/result/result.py @@ -202,6 +202,19 @@ def or_else(self, op: object) -> Ok[T]: """ return self + def inspect(self, op: Callable[[T], Any]) -> Result[T, E]: + """ + Calls a function with the contained value if `Ok`. Returns the original result. + """ + op(self._value) + return self + + def inspect_err(self, op: Callable[[E], Any]) -> Result[T, E]: + """ + Calls a function with the contained value if `Err`. Returns the original result. + """ + return self + class DoException(Exception): """ @@ -394,6 +407,19 @@ def or_else(self, op: Callable[[E], Result[T, F]]) -> Result[T, F]: """ return op(self._value) + def inspect(self, op: Callable[[T], Any]) -> Result[T, E]: + """ + Calls a function with the contained value if `Ok`. Returns the original result. + """ + return self + + def inspect_err(self, op: Callable[[E], Any]) -> Result[T, E]: + """ + Calls a function with the contained value if `Err`. Returns the original result. + """ + op(self._value) + return self + # define Result as a generic type alias for use # in type annotations diff --git a/tests/test_result.py b/tests/test_result.py index f0e05c6..c99c241 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -213,6 +213,36 @@ def test_and_then() -> None: assert Err(3).and_then(sq_lambda).and_then(sq_lambda).err() == 3 +def test_inspect() -> None: + oks: list[int] = [] + add_to_oks: Callable[[int], None] = lambda x: oks.append(x) + + assert Ok(2).inspect(add_to_oks) == Ok(2) + assert Err("e").inspect(add_to_oks) == Err("e") + assert oks == [2] + + +def test_inspect_err() -> None: + errs: list[str] = [] + add_to_errs: Callable[[str], None] = lambda x: errs.append(x) + + assert Ok(2).inspect_err(add_to_errs) == Ok(2) + assert Err("e").inspect_err(add_to_errs) == Err("e") + assert errs == ["e"] + + +def test_inspect_regular_fn() -> None: + oks: list[str] = [] + + def _add_to_oks(x: str) -> str: + oks.append(x) + return x + x + + assert Ok("hello").inspect(_add_to_oks) == Ok("hello") + assert Err("error").inspect(_add_to_oks) == Err("error") + assert oks == ["hello"] + + @pytest.mark.asyncio async def test_and_then_async() -> None: assert (await (await Ok(2).and_then_async(sq_async)).and_then_async(sq_async)).ok() == 16