|
from typing import Optional, List, Tuple, Callable, Any |
|
|
|
from .base import ILoaderClass, Loader, CAPTURE_EXCEPTIONS |
|
from .exception import CompositeStructureError |
|
from .types import method |
|
|
|
COLLECTION_ERROR_ITEM = Tuple[int, Exception] |
|
COLLECTION_ERRORS = List[COLLECTION_ERROR_ITEM] |
|
|
|
|
|
class CollectionError(CompositeStructureError): |
|
""" |
|
Overview: |
|
Collection error. |
|
Interfaces: |
|
``__init__``, ``errors`` |
|
Properties: |
|
``errors`` |
|
""" |
|
|
|
def __init__(self, errors: COLLECTION_ERRORS): |
|
""" |
|
Overview: |
|
Initialize the CollectionError. |
|
Arguments: |
|
- errors (:obj:`COLLECTION_ERRORS`): The errors. |
|
""" |
|
|
|
self.__errors = list(errors or []) |
|
CompositeStructureError.__init__( |
|
self, '{count} error(s) found in collection.'.format(count=repr(list(self.__errors))) |
|
) |
|
|
|
@property |
|
def errors(self) -> COLLECTION_ERRORS: |
|
""" |
|
Overview: |
|
Get the errors. |
|
""" |
|
|
|
return self.__errors |
|
|
|
|
|
def collection(loader, type_back: bool = True) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a collection loader. |
|
Arguments: |
|
- loader (:obj:`ILoaderClass`): The loader. |
|
- type_back (:obj:`bool`): Whether to convert the type back. |
|
""" |
|
|
|
loader = Loader(loader) |
|
|
|
def _load(value): |
|
_result = [] |
|
_errors = [] |
|
|
|
for index, item in enumerate(value): |
|
try: |
|
_return = loader.load(item) |
|
except CAPTURE_EXCEPTIONS as err: |
|
_errors.append((index, err)) |
|
else: |
|
_result.append(_return) |
|
|
|
if _errors: |
|
raise CollectionError(_errors) |
|
|
|
if type_back: |
|
_result = type(value)(_result) |
|
return _result |
|
|
|
return method('__iter__') & Loader(_load) |
|
|
|
|
|
def tuple_(*loaders) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a tuple loader. |
|
Arguments: |
|
- loaders (:obj:`tuple`): The loaders. |
|
""" |
|
|
|
loaders = [Loader(loader) for loader in loaders] |
|
|
|
def _load(value: tuple): |
|
return tuple([loader(item) for loader, item in zip(loaders, value)]) |
|
|
|
return tuple & length_is(len(loaders)) & Loader(_load) |
|
|
|
|
|
def length(min_length: Optional[int] = None, max_length: Optional[int] = None) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a length loader. |
|
Arguments: |
|
- min_length (:obj:`int`): The minimum length. |
|
- max_length (:obj:`int`): The maximum length. |
|
""" |
|
|
|
def _load(value): |
|
_length = len(value) |
|
if min_length is not None and _length < min_length: |
|
raise ValueError( |
|
'minimum length is {expect}, but {actual} found'.format(expect=repr(min_length), actual=repr(_length)) |
|
) |
|
if max_length is not None and _length > max_length: |
|
raise ValueError( |
|
'maximum length is {expect}, but {actual} found'.format(expect=repr(max_length), actual=repr(_length)) |
|
) |
|
|
|
return value |
|
|
|
return method('__len__') & Loader(_load) |
|
|
|
|
|
def length_is(length_: int) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a length loader. |
|
Arguments: |
|
- length_ (:obj:`int`): The length. |
|
""" |
|
|
|
return length(min_length=length_, max_length=length_) |
|
|
|
|
|
def contains(content) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a contains loader. |
|
Arguments: |
|
- content (:obj:`Any`): The content. |
|
""" |
|
|
|
def _load(value): |
|
if content not in value: |
|
raise ValueError('{content} not found in value'.format(content=repr(content))) |
|
|
|
return value |
|
|
|
return method('__contains__') & Loader(_load) |
|
|
|
|
|
def cofilter(checker: Callable[[Any], bool], type_back: bool = True) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a cofilter loader. |
|
Arguments: |
|
- checker (:obj:`Callable[[Any], bool]`): The checker. |
|
- type_back (:obj:`bool`): Whether to convert the type back. |
|
""" |
|
|
|
def _load(value): |
|
_result = [item for item in value if checker(item)] |
|
if type_back: |
|
_result = type(value)(_result) |
|
return _result |
|
|
|
return method('__iter__') & Loader(_load) |
|
|
|
|
|
def tpselector(*indices) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a tuple selector loader. |
|
Arguments: |
|
- indices (:obj:`tuple`): The indices. |
|
""" |
|
|
|
def _load(value: tuple): |
|
return tuple([value[index] for index in indices]) |
|
|
|
return tuple & Loader(_load) |
|
|