File size: 6,759 Bytes
079c32c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
from abc import abstractmethod
from typing import TypeVar, Callable, Any

CAPTURE_EXCEPTIONS = (Exception, )
_ValueType = TypeVar('_ValueType')


def _to_exception(exception) -> Callable[[Any], Exception]:
    """
    Overview:
        Convert exception to callable exception.
    Arguments:
        - exception (:obj:`Exception`): The exception to be converted.
    """

    if hasattr(exception, '__call__'):
        return exception
    elif isinstance(exception, Exception):
        return lambda v: exception
    elif isinstance(exception, str):
        return lambda v: ValueError(exception)
    else:
        raise TypeError(
            'Unknown type of exception, func, exception or str expected but {actual} found.'.format(
                actual=repr(type(exception).__name__)
            )
        )


def _to_loader(value) -> 'ILoaderClass':
    """
    Overview:
        Convert value to loader.
    Arguments:
        - value (:obj:`Any`): The value to be converted.
    """

    if isinstance(value, ILoaderClass):
        return value
    elif isinstance(value, tuple):
        if len(value) == 2:
            _predict, _exception = value
            _load = None
        elif len(value) == 3:
            _predict, _load, _exception = value
        else:
            raise ValueError('Tuple\'s length should be 2 or 3, but {actual} found.'.format(actual=repr(len(value))))

        _exception = _to_exception(_exception)

        def _load_tuple(value_):
            if not _predict(value_):
                raise _exception(value_)

            return (_load or (lambda v: v))(value_)

        return _to_loader(_load_tuple)
    elif isinstance(value, type):

        def _load_type(value_):
            if not isinstance(value_, value):
                raise TypeError(
                    'type not match, {expect} expected but {actual} found'.format(
                        expect=repr(value.__name__), actual=repr(type(value_).__name__)
                    )
                )
            return value_

        return _to_loader(_load_type)
    elif hasattr(value, '__call__'):

        class _Loader(ILoaderClass):

            def _load(self, value_):
                return value(value_)

        return _Loader()
    elif isinstance(value, bool):
        return _to_loader((lambda v: value, ValueError('assertion false')))
    elif value is None:
        return _to_loader(
            (
                lambda v: v is None, lambda v:
                TypeError('type not match, none expected but {actual} found'.format(actual=repr(type(v).__name__)))
            )
        )
    else:
        return _to_loader(lambda v: value)


Loader = _to_loader


def _reset_exception(loader, eg: Callable[[Any, Exception], Exception]):
    """
    Overview:
        Reset exception of loader.
    """

    loader = Loader(loader)

    def _load(value):
        try:
            return loader(value)
        except CAPTURE_EXCEPTIONS as err:
            raise eg(value, err)

    return Loader(_load)


class ILoaderClass:
    """
    Overview:
        Base class of loader.
    Interfaces:
        ``__init__``, ``_load``, ``load``, ``check``, ``__call__``, ``__and__``, ``__or__``, ``__rshift__``
    """

    @abstractmethod
    def _load(self, value: _ValueType) -> _ValueType:
        """
        Overview:
            Load the value.
        Arguments:
            - value (:obj:`_ValueType`): The value to be loaded.
        """

        raise NotImplementedError

    def __load(self, value: _ValueType) -> _ValueType:
        """
        Overview:
            Load the value.
        Arguments:
            - value (:obj:`_ValueType`): The value to be loaded.
        """

        return self._load(value)

    def __check(self, value: _ValueType) -> bool:
        """
        Overview:
            Check whether the value is valid.
        Arguments:
            - value (:obj:`_ValueType`): The value to be checked.
        """

        try:
            self._load(value)
        except CAPTURE_EXCEPTIONS:
            return False
        else:
            return True

    def load(self, value: _ValueType) -> _ValueType:
        """
        Overview:
            Load the value.
        Arguments:
            - value (:obj:`_ValueType`): The value to be loaded.
        """

        return self.__load(value)

    def check(self, value: _ValueType) -> bool:
        """
        Overview:
            Check whether the value is valid.
        Arguments:
            - value (:obj:`_ValueType`): The value to be checked.
        """

        return self.__check(value)

    def __call__(self, value: _ValueType) -> _ValueType:
        """
        Overview:
            Load the value.
        Arguments:
            - value (:obj:`_ValueType`): The value to be loaded.
        """

        return self.__load(value)

    def __and__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        def _load(value: _ValueType) -> _ValueType:
            self.load(value)
            return Loader(other).load(value)

        return Loader(_load)

    def __rand__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        return Loader(other) & self

    def __or__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        def _load(value: _ValueType) -> _ValueType:
            try:
                return self.load(value)
            except CAPTURE_EXCEPTIONS:
                return Loader(other).load(value)

        return Loader(_load)

    def __ror__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        return Loader(other) | self

    def __rshift__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        def _load(value: _ValueType) -> _ValueType:
            _return_value = self.load(value)
            return _to_loader(other).load(_return_value)

        return Loader(_load)

    def __rrshift__(self, other) -> 'ILoaderClass':
        """
        Overview:
            Combine two loaders.
        Arguments:
            - other (:obj:`ILoaderClass`): The other loader.
        """

        return Loader(other) >> self