Source code for returns.pipeline
# -*- coding: utf-8 -*-
from functools import wraps
from inspect import iscoroutinefunction
from typing import Callable, Coroutine, Type, TypeVar, Union, overload
from typing_extensions import Protocol
from returns._generated.pipe import _pipe as pipe # noqa: F401, WPS436
from returns.maybe import Maybe
from returns.primitives.exceptions import UnwrapFailedError
from returns.result import Result
# Logical aliases:
_Unwrapable = Union[Result, Maybe]
# Just aliases:
_FirstType = TypeVar('_FirstType')
_SecondType = TypeVar('_SecondType')
# Hacks for functions:
_ReturnResultType = TypeVar(
'_ReturnResultType',
bound=Callable[..., Result],
)
_AsyncReturnResultType = TypeVar(
'_AsyncReturnResultType',
bound=Callable[..., Coroutine[_FirstType, _SecondType, Result]],
)
_ReturnMaybeType = TypeVar(
'_ReturnMaybeType',
bound=Callable[..., Maybe],
)
_AsyncReturnMaybeType = TypeVar(
'_AsyncReturnMaybeType',
bound=Callable[..., Coroutine[_FirstType, _SecondType, Maybe]],
)
[docs]def is_successful(container: _Unwrapable) -> bool:
"""
Determins if a container was successful or not.
We treat container that raise ``UnwrapFailedError`` on ``.unwrap()``
not successful.
.. code:: python
>>> from returns.maybe import Some, Nothing
>>> from returns.result import Failure, Success
>>> is_successful(Some(1))
True
>>> is_successful(Nothing)
False
>>> is_successful(Success(1))
True
>>> is_successful(Failure(1))
False
"""
try:
container.unwrap()
except UnwrapFailedError:
return False
else:
return True
class _PipelineResultProtocol(Protocol):
@overload
def __call__(self, function: _ReturnResultType) -> _ReturnResultType:
"""Sync pipeline case for ``Result`` container."""
@overload # noqa: F811
def __call__(
self, function: _AsyncReturnResultType,
) -> _AsyncReturnResultType:
"""Async pipeline case for ``Result`` container."""
class _PipelineMaybeProtocol(Protocol):
@overload
def __call__(self, function: _ReturnMaybeType) -> _ReturnMaybeType:
"""Sync pipeline case for ``Maybe`` container."""
@overload # noqa: F811
def __call__(
self, function: _AsyncReturnMaybeType,
) -> _AsyncReturnMaybeType:
"""Async pipeline case for ``Maybe`` container."""
@overload
def pipeline(
container_type: Type[Result],
) -> _PipelineResultProtocol:
"""Pipeline case for ``Result`` container."""
@overload
def pipeline(
container_type: Type[Maybe],
) -> _PipelineMaybeProtocol:
"""Pipeline case for ``Maybe`` container."""
[docs]def pipeline(container_type): # noqa: C901, WPS212
"""
Decorator to enable ``do-notation`` context.
Should be used for series of computations that rely on ``.unwrap`` method.
Supports both async and regular functions.
Works with both ``Maybe`` and ``Result`` containers.
Example:
.. code:: python
>>> from typing import Optional
>>> @pipeline(Maybe)
... def test(one: Optional[int], two: Optional[int]) -> Maybe[int]:
... first = Maybe.new(one).unwrap()
... second = Maybe.new(two).unwrap()
... return Maybe.new(first + second)
...
>>> str(test(1, 2))
'<Some: 3>'
>>> str(test(2, None))
'<Nothing>'
Make sure to supply the correct container type when creating a pipeline.
"""
def factory(function):
if iscoroutinefunction(function):
async def decorator(*args, **kwargs): # noqa: WPS430
try:
return await function(*args, **kwargs)
except UnwrapFailedError as exc:
return exc.halted_container
else:
def decorator(*args, **kwargs): # noqa: WPS430
try:
return function(*args, **kwargs)
except UnwrapFailedError as exc:
return exc.halted_container
return wraps(function)(decorator)
return factory