from typing import Callable, ClassVar, Dict, Generic, Sequence, Type, TypeVar
from typing_extensions import final
from returns.primitives.types import Immutable
_Caps = TypeVar('_Caps')
_ReturnType = TypeVar('_ReturnType')
_TypeArgType1 = TypeVar('_TypeArgType1')
_TypeArgType2 = TypeVar('_TypeArgType2')
_TypeArgType3 = TypeVar('_TypeArgType3')
#: Special alias to define laws as functions even inside a class
law_definition = staticmethod
[docs]class Law(Immutable):
"""
Base class for all laws. Does not have an attached signature.
Should not be used directly.
Use ``Law1``, ``Law2`` or ``Law3`` instead.
"""
__slots__ = ('definition', )
#: Function used to define this law.
definition: Callable
def __init__(self, function) -> None:
"""Saves function to the inner state."""
object.__setattr__(self, 'definition', function) # noqa: WPS609
@final
@property
def name(self) -> str:
"""Returns a name of the given law. Basically a name of the function."""
return self.definition.__name__
[docs]@final
class Law1(
Law,
Generic[_TypeArgType1, _ReturnType],
):
"""Law definition for functions with a single argument."""
definition: Callable[['Law1', _TypeArgType1], _ReturnType]
def __init__(
self,
function: Callable[[_TypeArgType1], _ReturnType],
) -> None:
"""Saves function of one argument to the inner state."""
super().__init__(function)
[docs]@final
class Law2(
Law,
Generic[_TypeArgType1, _TypeArgType2, _ReturnType],
):
"""Law definition for functions with two arguments."""
definition: Callable[['Law2', _TypeArgType1, _TypeArgType2], _ReturnType]
def __init__(
self,
function: Callable[[_TypeArgType1, _TypeArgType2], _ReturnType],
) -> None:
"""Saves function of two arguments to the inner state."""
super().__init__(function)
[docs]@final
class Law3(
Law,
Generic[_TypeArgType1, _TypeArgType2, _TypeArgType3, _ReturnType],
):
"""Law definition for functions with three argument."""
definition: Callable[
['Law3', _TypeArgType1, _TypeArgType2, _TypeArgType3],
_ReturnType,
]
def __init__(
self,
function: Callable[
[_TypeArgType1, _TypeArgType2, _TypeArgType3],
_ReturnType,
],
) -> None:
"""Saves function of three arguments to the inner state."""
super().__init__(function)
[docs]class Lawful(Generic[_Caps]):
"""
Base class for all lawful classes.
Allows to smartly collect all defined laws from all parent classes.
"""
#: Some classes and interfaces might have laws, some might not have any.
_laws: ClassVar[Sequence[Law]]
[docs] @final # noqa: WPS210
@classmethod
def laws(cls) -> Dict[Type['Lawful'], Sequence[Law]]: # noqa: WPS210
"""
Collects all laws from all parent classes.
Algorithm:
1. First, we collect all unique parents in ``__mro__``
2. Then we get the laws definition from each of them
3. Then we structure them in a ``type: its_laws`` way
"""
seen = {
'{0}.{1}'.format(
parent.__module__, # noqa: WPS609
parent.__qualname__,
): parent
for parent in cls.__mro__
}
laws = {}
for klass in seen.values():
current_laws = klass.__dict__.get('_laws', ()) # noqa: WPS609
if not current_laws:
continue
laws[klass] = current_laws
return laws
[docs]class LawSpecDef(object):
"""Base class for all collection of laws aka LawSpecs."""
__slots__ = ()