import re from functools import wraps from itertools import islice from typing import Callable, Union, Pattern from .base import Loader, ILoaderClass STRING_PROCESSOR = Callable[[str], str] def enum(*items, case_sensitive: bool = True) -> ILoaderClass: """ Overview: Create an enum loader. Arguments: - items (:obj:`Iterable[str]`): The items. - case_sensitive (:obj:`bool`): Whether case sensitive. """ def _case_sensitive(func: STRING_PROCESSOR) -> STRING_PROCESSOR: if case_sensitive: return func else: @wraps(func) def _new_func(value: str) -> str: return func(value).lower() return _new_func @_case_sensitive def _item_process(value): return str(value) item_set = set([_item_process(item) for item in items]) def _load(value: str): real_value = _item_process(value) if real_value not in item_set: raise ValueError('unknown enum value {value}'.format(value=repr(value))) return real_value return Loader(_load) def _to_regexp(regexp) -> Pattern: """ Overview: Convert regexp to re.Pattern. Arguments: - regexp (:obj:`Union[str, re.Pattern]`): The regexp. """ if isinstance(regexp, Pattern): return regexp elif isinstance(regexp, str): return re.compile(regexp) else: raise TypeError( 'Regexp should be either str or re.Pattern but {actual} found.'.format(actual=repr(type(regexp).__name__)) ) def rematch(regexp: Union[str, Pattern]) -> ILoaderClass: """ Overview: Create a rematch loader. Arguments: - regexp (:obj:`Union[str, re.Pattern]`): The regexp. """ regexp = _to_regexp(regexp) def _load(value: str): if not regexp.fullmatch(value): raise ValueError( 'fully match with regexp {pattern} expected but {actual} found'.format( pattern=repr(regexp.pattern), actual=repr(value), ) ) return value return Loader(_load) def regrep(regexp: Union[str, Pattern], group: int = 0) -> ILoaderClass: """ Overview: Create a regrep loader. Arguments: - regexp (:obj:`Union[str, re.Pattern]`): The regexp. - group (:obj:`int`): The group. """ regexp = _to_regexp(regexp) def _load(value: str): results = list(islice(regexp.finditer(value), 1)) if results: return results[0][group] else: raise ValueError( 'fully match with regexp {pattern} expected but {actual} found'.format( pattern=repr(regexp.pattern), actual=repr(value), ) ) return Loader(_load)