Source code for option.result

# MIT License

# Copyright (c) 2018-2022 Peijun Ma

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""This module contains the Result type."""
from typing import Any, Callable, Generic, Union

from option.option_ import NONE, Option
from option.types_ import E, F, T, U
from option.types_ import SupportsDunderLT, SupportsDunderGT
from option.types_ import SupportsDunderLE, SupportsDunderGE


[docs]class Result(Generic[T, E]): """ :class:`Result` is a type that either success (:meth:`Result.Ok`) or failure (:meth:`Result.Err`). To create an Ok value, use :meth:`Result.Ok` or :func:`Ok`. To create a Err value, use :meth:`Result.Err` or :func:`Err`. Calling the :class:`Result` constructor directly will raise a ``TypeError``. Examples: >>> Result.Ok(1) Ok(1) >>> Result.Err('Fail!') Err('Fail!') """ __slots__ = ('_val', '_is_ok', '_type') def __init__(self, val: Union[T, E], is_ok: bool, *, _force: bool = False) -> None: if not _force: raise TypeError( 'Cannot directly initialize, ' 'please use one of the factory functions instead.' ) self._val = val self._is_ok = is_ok self._type = type(self)
[docs] @classmethod def Ok(cls, val: T) -> 'Result[T, Any]': """ Contains the success value. Args: val: The success value. Returns: The :class:`Result` containing the success value. Examples: >>> res = Result.Ok(1) >>> res Ok(1) >>> res.is_ok True """ return cls(val, True, _force=True)
[docs] @classmethod def Err(cls, err: E) -> 'Result[Any, E]': """ Contains the error value. Args: err: The error value. Returns: The :class:`Result` containing the error value. Examples: >>> res = Result.Err('Oh No') >>> res Err('Oh No') >>> res.is_err True """ return cls(err, False, _force=True)
def __bool__(self) -> bool: return self._is_ok @property def is_ok(self) -> bool: """ Returns `True` if the result is :meth:`Result.Ok`. Examples: >>> Ok(1).is_ok True >>> Err(1).is_ok False """ return self._is_ok @property def is_err(self) -> bool: """ Returns `True` if the result is :meth:`Result.Err`. Examples: >>> Ok(1).is_err False >>> Err(1).is_err True """ return not self._is_ok
[docs] def ok(self) -> Option[T]: """ Converts from :class:`Result` [T, E] to :class:`option.option_.Option` [T]. Returns: :class:`Option` containing the success value if `self` is :meth:`Result.Ok`, otherwise :data:`option.option_.NONE`. Examples: >>> Ok(1).ok() Some(1) >>> Err(1).ok() NONE """ return Option.Some(self._val) if self._is_ok else NONE # type: ignore
[docs] def err(self) -> Option[E]: """ Converts from :class:`Result` [T, E] to :class:`option.option_.Option` [E]. Returns: :class:`Option` containing the error value if `self` is :meth:`Result.Err`, otherwise :data:`option.option_.NONE`. Examples: >>> Ok(1).err() NONE >>> Err(1).err() Some(1) """ return NONE if self._is_ok else Option.Some(self._val) # type: ignore
[docs] def map(self, op: Callable[[T], U]) -> 'Union[Result[U, E], Result[T, E]]': """ Applies a function to the contained :meth:`Result.Ok` value. Args: op: The function to apply to the :meth:`Result.Ok` value. Returns: A :class:`Result` with its success value as the function result if `self` is an :meth:`Result.Ok` value, otherwise returns `self`. Examples: >>> Ok(1).map(lambda x: x * 2) Ok(2) >>> Err(1).map(lambda x: x * 2) Err(1) """ return self._type.Ok(op(self._val)) if self._is_ok else self # type: ignore
[docs] def flatmap(self, op: 'Callable[[T], Result[U, E]]') -> 'Result[U, E]': """ Applies a function to the contained :meth:`Result.Ok` value. This is different than :meth:`Result.map` because the function result is not wrapped in a new :class:`Result`. Args: op: The function to apply to the contained :meth:`Result.Ok` value. Returns: The result of the function if `self` is an :meth:`Result.Ok` value, otherwise returns `self`. Examples: >>> def sq(x): return Ok(x * x) >>> def err(x): return Err(x) >>> Ok(2).flatmap(sq).flatmap(sq) Ok(16) >>> Ok(2).flatmap(sq).flatmap(err) Err(4) >>> Ok(2).flatmap(err).flatmap(sq) Err(2) >>> Err(3).flatmap(sq).flatmap(sq) Err(3) """ return op(self._val) if self._is_ok else self # type: ignore
[docs] def map_err(self, op: Callable[[E], F]) -> 'Union[Result[T, F], Result[T, E]]': """ Applies a function to the contained :meth:`Result.Err` value. Args: op: The function to apply to the :meth:`Result.Err` value. Returns: A :class:`Result` with its error value as the function result if `self` is a :meth:`Result.Err` value, otherwise returns `self`. Examples: >>> Ok(1).map_err(lambda x: x * 2) Ok(1) >>> Err(1).map_err(lambda x: x * 2) Err(2) """ return self if self._is_ok else self._type.Err(op(self._val)) # type: ignore
[docs] def unwrap(self) -> T: """ Returns the success value in the :class:`Result`. Returns: The success value in the :class:`Result`. Raises: ``ValueError`` with the message provided by the error value if the :class:`Result` is a :meth:`Result.Err` value. Examples: >>> Ok(1).unwrap() 1 >>> try: ... Err(1).unwrap() ... except ValueError as e: ... print(e) 1 """ if self._is_ok: return self._val # type: ignore raise ValueError(self._val)
[docs] def unwrap_or(self, optb: T) -> T: """ Returns the success value in the :class:`Result` or ``optb``. Args: optb: The default return value. Returns: The success value in the :class:`Result` if it is a :meth:`Result.Ok` value, otherwise ``optb``. Notes: If you wish to use a result of a function call as the default, it is recommnded to use :meth:`unwrap_or_else` instead. Examples: >>> Ok(1).unwrap_or(2) 1 >>> Err(1).unwrap_or(2) 2 """ return self._val if self._is_ok else optb # type: ignore
[docs] def unwrap_or_else(self, op: Callable[[E], U]) -> Union[T, U]: """ Returns the sucess value in the :class:`Result` or computes a default from the error value. Args: op: The function to computes default with. Returns: The success value in the :class:`Result` if it is a :meth:`Result.Ok` value, otherwise ``op(E)``. Examples: >>> Ok(1).unwrap_or_else(lambda e: e * 10) 1 >>> Err(1).unwrap_or_else(lambda e: e * 10) 10 """ return self._val if self._is_ok else op(self._val) # type: ignore
[docs] def expect(self, msg: object) -> T: """ Returns the success value in the :class:`Result` or raises a ``ValueError`` with a provided message. Args: msg: The error message. Returns: The success value in the :class:`Result` if it is a :meth:`Result.Ok` value. Raises: ``ValueError`` with ``msg`` as the message if the :class:`Result` is a :meth:`Result.Err` value. Examples: >>> Ok(1).expect('no') 1 >>> try: ... Err(1).expect('no') ... except ValueError as e: ... print(e) no """ if self._is_ok: return self._val # type: ignore raise ValueError(msg)
[docs] def unwrap_err(self) -> E: """ Returns the error value in a :class:`Result`. Returns: The error value in the :class:`Result` if it is a :meth:`Result.Err` value. Raises: ``ValueError`` with the message provided by the success value if the :class:`Result` is a :meth:`Result.Ok` value. Examples: >>> try: ... Ok(1).unwrap_err() ... except ValueError as e: ... print(e) 1 >>> Err('Oh No').unwrap_err() 'Oh No' """ if self._is_ok: raise ValueError(self._val) return self._val # type: ignore
[docs] def expect_err(self, msg: object) -> E: """ Returns the error value in a :class:`Result`, or raises a ``ValueError`` with the provided message. Args: msg: The error message. Returns: The error value in the :class:`Result` if it is a :meth:`Result.Err` value. Raises: ``ValueError`` with the message provided by ``msg`` if the :class:`Result` is a :meth:`Result.Ok` value. Examples: >>> try: ... Ok(1).expect_err('Oh No') ... except ValueError as e: ... print(e) Oh No >>> Err(1).expect_err('Yes') 1 """ if self._is_ok: raise ValueError(msg) return self._val # type: ignore
def __repr__(self) -> str: return f'Ok({self._val!r})' if self._is_ok else f'Err({self._val!r})' def __hash__(self) -> int: return hash((self._type, self._is_ok, self._val)) def __eq__(self, other: object) -> bool: return (isinstance(other, self._type) and self._is_ok == other._is_ok and self._val == other._val) def __ne__(self, other: object) -> bool: return (not isinstance(other, self._type) or self._is_ok != other._is_ok or self._val != other._val) def __lt__(self: 'Result[SupportsDunderLT, SupportsDunderLT]', other: object) -> bool: if isinstance(other, self._type): if self._is_ok == other._is_ok: return self._val < other._val return self._is_ok return NotImplemented def __le__(self: 'Result[SupportsDunderLE, SupportsDunderLE]', other: object) -> bool: if isinstance(other, self._type): if self._is_ok == other._is_ok: return self._val <= other._val return self._is_ok return NotImplemented def __gt__(self: 'Result[SupportsDunderGT, SupportsDunderGT]', other: object) -> bool: if isinstance(other, self._type): if self._is_ok == other._is_ok: return self._val > other._val return other._is_ok return NotImplemented def __ge__(self: 'Result[SupportsDunderGE, SupportsDunderGE]', other: object) -> bool: if isinstance(other, self._type): if self._is_ok == other._is_ok: return self._val >= other._val return other._is_ok return NotImplemented
[docs]def Ok(val: T) -> Result[T, Any]: """Shortcut function for :meth:`Result.Ok`.""" return Result.Ok(val)
[docs]def Err(err: E) -> Result[Any, E]: """Shortcut function for :meth:`Result.Err`.""" return Result.Err(err)
if __name__ == '__main__': import doctest doctest.testmod()