File: laws.py

package info (click to toggle)
python-returns 0.26.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,652 kB
  • sloc: python: 11,000; makefile: 18
file content (147 lines) | stat: -rw-r--r-- 3,717 bytes parent folder | download | duplicates (2)
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__ = ()