zjowowen's picture
init space
079c32c
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)