1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
from collections.abc import Callable, Sequence
from typing import ClassVar, Final, Generic, TypeVar, 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
LAWS_ATTRIBUTE: Final = '_laws'
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)
@final
@property
def name(self) -> str:
"""Returns a name of the given law. Basically a name of the function."""
return self.definition.__name__
@final
class Law1(
Law,
Generic[_TypeArgType1, _ReturnType],
):
"""Law definition for functions with a single argument."""
__slots__ = ()
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)
@final
class Law2(
Law,
Generic[_TypeArgType1, _TypeArgType2, _ReturnType],
):
"""Law definition for functions with two arguments."""
__slots__ = ()
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)
@final
class Law3(
Law,
Generic[_TypeArgType1, _TypeArgType2, _TypeArgType3, _ReturnType],
):
"""Law definition for functions with three argument."""
__slots__ = ()
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)
class Lawful(Generic[_Caps]):
"""
Base class for all lawful classes.
Allows to smartly collect all defined laws from all parent classes.
"""
__slots__ = ()
#: Some classes and interfaces might have laws, some might not have any.
_laws: ClassVar[Sequence[Law]]
@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 = {
f'{parent.__module__}.{parent.__qualname__}': parent
for parent in cls.__mro__
}
laws = {}
for klass in seen.values():
current_laws = klass.__dict__.get(LAWS_ATTRIBUTE, ())
if not current_laws:
continue
laws[klass] = current_laws
return laws
class LawSpecDef:
"""Base class for all collection of laws aka LawSpecs."""
__slots__ = ()
|