Created
February 1, 2018 14:22
-
-
Save iAnanich/622be82fd762fcac26cf718501df953b to your computer and use it in GitHub Desktop.
set of FieldsStorage classes which behaves like mutable mapping with preset set of keys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import typing | |
TypeOrNone = typing.Union[type, None] | |
def has_wrong_type(obj, expected_obj_type: TypeOrNone) -> bool: | |
""" | |
Checks if given `obj` object has not given `expected_obj_type` type. If | |
`expected_obj_type` is `None` than it will return `True` | |
:param obj: any object | |
:param expected_obj_type: expected type of the object or `None` | |
:return: `True` if `obj` object is not of `expected_obj_type` type, `False` | |
if `expected_obj_type` is `None` or `obj` object has `expected_obj_type` type | |
""" | |
# if `expected` type is `None` it will | |
# return False without `isinstance` call | |
return expected_obj_type is not None and not isinstance(obj, expected_obj_type) | |
def raise_type_error(obj_repr: str, obj_type: type, expected_obj_type: type, | |
obj_name: str ='This'): | |
raise TypeError( | |
f'{obj_name} {obj_repr} has "{obj_type}" type while ' | |
f'"{expected_obj_type}" is expected.' | |
) | |
def check_obj_type(obj, expected_obj_type: TypeOrNone, obj_name: str ='object'): | |
if has_wrong_type(obj=obj, expected_obj_type=expected_obj_type): | |
raise_type_error( | |
obj_name=obj_name, | |
obj_repr=repr(obj), | |
obj_type=type(obj), | |
expected_obj_type=expected_obj_type, | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import abc | |
from typing import FrozenSet, Dict, NoReturn | |
from .check import check_object_type | |
class FieldsStorageABC(abc.ABC): | |
""" | |
Defines interface for `FieldsStorage` classes. | |
""" | |
FieldNameType: type = object | |
FieldValueType: type = object | |
FieldsDictType: type = Dict[FieldNameType, FieldValueType] | |
@abc.abstractmethod | |
def __init__(self, field_names: FrozenSet[FieldNameType]): | |
self._all_field_names: frozenset = frozenset(field_names) | |
self._filled_field_names: set = set() | |
@abc.abstractmethod | |
def update(self, dictionary: FieldsDictType) -> NoReturn: | |
""" | |
Behaves as ``dict.update`` method. | |
:param dictionary: | |
:return: | |
""" | |
pass | |
@abc.abstractmethod | |
def dict_copy(self) -> FieldsDictType: | |
""" | |
Returns dictionary copy | |
:return: | |
""" | |
pass | |
@abc.abstractmethod | |
def set(self, field_name: FieldNameType, field_value: FieldValueType) -> NoReturn: | |
""" | |
Validates field and value, then sets field's value if given ``field`` | |
is allowed by all fields set, and updates set of filled fields. | |
:param field_name: ``str`` | |
:param field_value: ``str`` | |
:return: ``None`` | |
""" | |
pass | |
@abc.abstractmethod | |
def get(self, field_name: FieldNameType) -> FieldValueType: | |
""" | |
Validates field and returns it's value. | |
:param field_name: field's name | |
:raise KeyError: if no such ``field_name`` was found. | |
:return: related value | |
""" | |
pass | |
@abc.abstractmethod | |
def reset(self) -> NoReturn: | |
""" | |
Drops fields' values, and clears set of filled fields. | |
:return: ``None`` | |
""" | |
pass | |
@abc.abstractmethod | |
def release(self) -> FieldsDictType: | |
""" | |
Returns dict copy and clears storage. | |
:return: field name to it's value mapping. | |
""" | |
pass | |
@abc.abstractmethod | |
def is_full(self) -> bool: | |
""" | |
Checks whether or not all fields were filled wis valid values. | |
:return: ``True`` if all fields has their values. | |
""" | |
pass | |
@abc.abstractmethod | |
def has_value(self, field_name: FieldNameType) -> bool: | |
""" | |
Checks whether or net given ``field`` filed has it's value. | |
:param field_name: one of the allowed fields. | |
:return: ``True`` if field has value. | |
""" | |
@abc.abstractmethod | |
def validate_field_name(self, field_name) -> FieldNameType: | |
""" | |
Validates field name, by just searching for it in set of allowed fields. | |
:param field_name: field name to validate | |
:raise KeyError: if no such ``field_name`` was found. | |
:return: validated field name | |
""" | |
pass | |
@abc.abstractmethod | |
def validate_field_value(self, field_value, *, | |
valid_field_name: FieldNameType) -> FieldValueType: | |
""" | |
Validates value. Uses ``valid_field_name`` for error messages. | |
:param field_value: value to validate | |
:param valid_field_name: valid field name. | |
:raise ValueError: no such case yet | |
:raise TypeError: if ``field_value`` is not an instance of ``FieldValueType`` | |
:return: validated value | |
""" | |
pass | |
class BaseStringFieldsStorage(FieldsStorageABC, metaclass=abc.ABCMeta): | |
FieldNameType = str | |
FieldValueType = str | |
FieldsDictType = Dict[FieldNameType, FieldValueType] | |
def __init__(self, field_names: FrozenSet[FieldNameType]): | |
for f in field_names: | |
check_obj_type(f, self.FieldNameType, f'Field') | |
self._all_field_names: frozenset = frozenset(field_names) | |
self._filled_field_names: set = set() | |
# do nothing | |
super().__init__(field_names) | |
def validate_field_name(self, field_name) -> FieldNameType: | |
if field_name not in self._all_field_names: | |
raise KeyError( | |
f'No such "{field_name}" field name found.' | |
) | |
return field_name | |
def validate_field_value(self, field_value, *, | |
valid_field_name: FieldNameType) -> FieldValueType: | |
check_obj_type(field_value, str, f'Value of the "{valid_field_name}" field.') | |
return field_value | |
def set(self, field_name: FieldNameType, field_value: FieldValueType) -> NoReturn: | |
valid_field_name = self.validate_field_name(field_name) | |
valid_field_value = self.validate_field_value( | |
field_value, valid_field_name=valid_field_name) | |
self._set(valid_field=valid_field_name, valid_value=valid_field_value) | |
self._filled_field_names.add(valid_field_name) | |
def get(self, field: FieldNameType) -> FieldValueType: | |
valid_field = self.validate_field_name(field) | |
return self._get(valid_field) | |
def update(self, dictionary: FieldsDictType) -> NoReturn: | |
for key, value in dictionary.items(): | |
self.set(field_name=key, field_value=value) | |
def reset(self) -> NoReturn: | |
self._reset() | |
self._filled_field_names.clear() | |
def release(self) -> FieldsDictType: | |
res = self.dict_copy() | |
self.reset() | |
return res | |
def has_value(self, field_name: FieldNameType) -> bool: | |
if field_name in self._filled_field_names: | |
return True | |
elif field_name in self._all_field_names: | |
return False | |
else: | |
raise KeyError(f'Unknown field: "{field_name}.') | |
def is_full(self) -> bool: | |
return self._filled_field_names == self._all_field_names | |
def dict_copy(self) -> dict: | |
return {field_name: self.get(field_name) | |
for field_name in self._filled_field_names} | |
@abc.abstractmethod | |
def _set(self, valid_field: FieldNameType, valid_value: FieldValueType) -> NoReturn: | |
""" | |
Abstraction over implementation details for setting ``field``'s | |
``value`` without overriding it. | |
:param valid_field: validated field | |
:param valid_value: validated value | |
:return: | |
""" | |
pass | |
@abc.abstractmethod | |
def _reset(self) -> NoReturn: | |
""" | |
Drops fields' values. | |
:return: ``None`` | |
""" | |
pass | |
@abc.abstractmethod | |
def _get(self, field_name: FieldNameType) -> FieldValueType: | |
""" | |
Returns value related to given ``field`` field. | |
:raise KeyError: if no such ``field_name`` was found. | |
:return: related value | |
""" | |
pass | |
class DictStringsFieldsStorage(BaseStringFieldsStorage): | |
def __init__(self, field_names: typing.FrozenSet[str]): | |
super().__init__(field_names) | |
self._storage: dict = self._new_storage() | |
def _new_storage(self) -> dict: | |
return {k: None for k in self._all_field_names} | |
def _reset(self): | |
self._storage.clear() | |
def _set(self, valid_field_name: str, valid_field_value: str): | |
self._storage[valid_field_name] = valid_field_value | |
def _get(self, field_name: str) -> str: | |
return self._storage[field_name] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment