|
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 |
|
|