|
from typing import List, Tuple, Callable, Any |
|
|
|
from .base import ILoaderClass, Loader, CAPTURE_EXCEPTIONS |
|
from .exception import CompositeStructureError |
|
from .types import method |
|
from .utils import raw |
|
|
|
MAPPING_ERROR_ITEM = Tuple[str, Exception] |
|
MAPPING_ERRORS = List[MAPPING_ERROR_ITEM] |
|
|
|
|
|
class MappingError(CompositeStructureError): |
|
""" |
|
Overview: |
|
Mapping error. |
|
Interfaces: |
|
``__init__``, ``errors`` |
|
""" |
|
|
|
def __init__(self, key_errors: MAPPING_ERRORS, value_errors: MAPPING_ERRORS): |
|
""" |
|
Overview: |
|
Initialize the MappingError. |
|
Arguments: |
|
- key_errors (:obj:`MAPPING_ERRORS`): The key errors. |
|
- value_errors (:obj:`MAPPING_ERRORS`): The value errors. |
|
""" |
|
|
|
self.__key_errors = list(key_errors or []) |
|
self.__value_errors = list(value_errors or []) |
|
self.__errors = self.__key_errors + self.__value_errors |
|
|
|
def key_errors(self) -> MAPPING_ERRORS: |
|
""" |
|
Overview: |
|
Get the key errors. |
|
""" |
|
|
|
return self.__key_errors |
|
|
|
def value_errors(self) -> MAPPING_ERRORS: |
|
""" |
|
Overview: |
|
Get the value errors. |
|
""" |
|
|
|
return self.__value_errors |
|
|
|
def errors(self) -> MAPPING_ERRORS: |
|
""" |
|
Overview: |
|
Get the errors. |
|
""" |
|
|
|
return self.__errors |
|
|
|
|
|
def mapping(key_loader, value_loader, type_back: bool = True) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a mapping loader. |
|
Arguments: |
|
- key_loader (:obj:`ILoaderClass`): The key loader. |
|
- value_loader (:obj:`ILoaderClass`): The value loader. |
|
- type_back (:obj:`bool`): Whether to convert the type back. |
|
""" |
|
|
|
key_loader = Loader(key_loader) |
|
value_loader = Loader(value_loader) |
|
|
|
def _load(value): |
|
_key_errors = [] |
|
_value_errors = [] |
|
_result = {} |
|
for key_, value_ in value.items(): |
|
key_error, value_error = None, None |
|
key_result, value_result = None, None |
|
|
|
try: |
|
key_result = key_loader(key_) |
|
except CAPTURE_EXCEPTIONS as err: |
|
key_error = err |
|
|
|
try: |
|
value_result = value_loader(value_) |
|
except CAPTURE_EXCEPTIONS as err: |
|
value_error = err |
|
|
|
if not key_error and not value_error: |
|
_result[key_result] = value_result |
|
else: |
|
if key_error: |
|
_key_errors.append((key_, key_error)) |
|
if value_error: |
|
_value_errors.append((key_, value_error)) |
|
|
|
if not _key_errors and not _value_errors: |
|
if type_back: |
|
_result = type(value)(_result) |
|
return _result |
|
else: |
|
raise MappingError(_key_errors, _value_errors) |
|
|
|
return method('items') & Loader(_load) |
|
|
|
|
|
def mpfilter(check: Callable[[Any, Any], bool], type_back: bool = True) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a mapping filter loader. |
|
Arguments: |
|
- check (:obj:`Callable[[Any, Any], bool]`): The check function. |
|
- type_back (:obj:`bool`): Whether to convert the type back. |
|
""" |
|
|
|
def _load(value): |
|
_result = {key_: value_ for key_, value_ in value.items() if check(key_, value_)} |
|
|
|
if type_back: |
|
_result = type(value)(_result) |
|
return _result |
|
|
|
return method('items') & Loader(_load) |
|
|
|
|
|
def mpkeys() -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a mapping keys loader. |
|
""" |
|
|
|
return method('items') & method('keys') & Loader(lambda v: set(v.keys())) |
|
|
|
|
|
def mpvalues() -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a mapping values loader. |
|
""" |
|
|
|
return method('items') & method('values') & Loader(lambda v: set(v.values())) |
|
|
|
|
|
def mpitems() -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a mapping items loader. |
|
""" |
|
|
|
return method('items') & Loader(lambda v: set([(key, value) for key, value in v.items()])) |
|
|
|
|
|
_INDEX_PRECHECK = method('__getitem__') |
|
|
|
|
|
def item(key) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a item loader. |
|
Arguments: |
|
- key (:obj:`Any`): The key. |
|
""" |
|
|
|
return _INDEX_PRECHECK & Loader( |
|
(lambda v: key in v.keys(), lambda v: v[key], KeyError('key {key} not found'.format(key=repr(key)))) |
|
) |
|
|
|
|
|
def item_or(key, default) -> ILoaderClass: |
|
""" |
|
Overview: |
|
Create a item or loader. |
|
Arguments: |
|
- key (:obj:`Any`): The key. |
|
- default (:obj:`Any`): The default value. |
|
""" |
|
|
|
return _INDEX_PRECHECK & (item(key) | raw(default)) |
|
|