# Type annotations
# pylint: disable=unused-import,wrong-import-order
from typing import cast, Any, Generic, Optional, TypeVar, Union # noqa
#
# Result and its handling.
#
#
# A ``Result`` can be either ``Ok(value)`` - valid result, contains a meaningful
# value - or ``Error(error)`` which represents an error, carrying error's description.
#
# T represents the type of valid value...
# pylint: disable=invalid-name
T = TypeVar("T")
# ... and E represents type of the error description.
# pylint: disable=invalid-name
E = TypeVar("E")
[docs]class Result(Generic[T, E]):
"""
A simple `Result` type inspired by Rust.
A ``Result`` can be either ``Ok(value)`` - valid result, contains a meaningful
value - or ``Error(error)`` which represents an error, carrying error's description.
:param bool _is_ok: ``True`` when the ``_value`` is OK-ish.
:param _value: the value carried by the result.
:param bool _force: guards against accidental direct use.
"""
def __init__(self, _is_ok, _value, _force=False):
# type: (bool, Union[T, E], bool) -> None
"""
.. warning::
Do not instantiate ``Result`` instances directly, **always** use either
:py:func:`Ok` or :py:func:`Error` functions. Otherwise, type guarantees
cannot be given.
"""
if not _force:
raise RuntimeError('Do not instantiate Result objects directly.')
self._is_ok = _is_ok
self._value = _value
def __eq__(self, other):
# type: (Any) -> bool
# pylint: disable=protected-access
# pylint: disable=line-too-long
return bool(
self.__class__ == other.__class__ and self.is_ok == cast(Result[T, E], other).is_ok and self._value == other._value # Ignore: PEP8Bear
)
def __ne__(self, other):
# type: (Any) -> bool
return not bool(self == other)
def __hash__(self):
# type: () -> int
return hash((self.is_ok, self._value))
def __repr__(self):
# type: () -> str
if self.is_ok:
return 'Ok({})'.format(repr(self._value))
return 'Error({})'.format(repr(self._value))
# pylint: disable=invalid-name
[docs] @classmethod
def Ok(cls, value):
# type: (T) -> Result[T, E]
return cls(_is_ok=True, _value=value, _force=True)
# pylint: disable=invalid-name
[docs] @classmethod
def Error(cls, error):
# type: (E) -> Result[T, E]
return cls(_is_ok=False, _value=error, _force=True)
@property
def is_ok(self):
# type: () -> bool
return self._is_ok
@property
def is_error(self):
# type: () -> bool
"""
Returns ``True`` if the result value is invalid.
"""
return not self._is_ok
# pylint: disable=invalid-name
@property
def ok(self):
# type: () -> Optional[T]
"""
Return the result value - valid - if it is valid. Otherwise,
``None`` is returned.
"""
return cast(T, self._value) if self.is_ok else None
@property
def error(self):
# type: () -> Optional[E]
"""
Return the result value - error - if it is invalid. Otherwise, ``None``
is returned.
"""
return cast(E, self._value) if self.is_error else None
@property
def value(self):
# type: () -> Union[T, E]
"""
Return the result value. It will be either one of valid and error types.
"""
return self._value
[docs] def expect(self, message):
# type: (str) -> T
"""
Return the result value if it is valid. Otherwise, an exception is raised.
"""
if self.is_ok:
return cast(T, self._value)
# Avoiding cyclic imports...
# pylint: disable=cyclic-import
from .glue import GlueError
raise GlueError(message)
[docs] def expect_error(self, message):
# type: (str) -> E
"""
Return the result value if it is invalid. Otherwise, an exception is raised.
"""
if self.is_error:
return cast(E, self._value)
# Avoiding cyclic imports...
# pylint: disable=cyclic-import
from .glue import GlueError
raise GlueError(message)
[docs] def unwrap(self):
# type: () -> T
"""
Return the result value if it is valid. Otherwise, an exception is rised.
"""
return self.expect('Expected valid result value, found error')
[docs] def unwrap_error(self):
# type: () -> E
"""
Return the error value if the result is invalid. Othwerise, an exception is raised.
"""
return self.expect_error('Expected invalid result value, found valid one')
[docs] def unwrap_or(self, default):
# type: (T) -> T
"""
Return the result value if it is valid. Otherwise, ``default`` is returned.
"""
if self.is_ok:
return cast(T, self._value)
return default
# pylint: disable=invalid-name
[docs]def Ok(value):
# type: (T) -> Result[T, E]
"""
Shortcut function to create a new valid Result.
"""
return Result.Ok(value)
# pylint: disable=invalid-name
[docs]def Error(error):
# type: (E) -> Result[T, E]
"""
Shortcut function to create a new error Result.
"""
return Result.Error(error)