Source code for classical.fields.base

import abc
from typing import Any, Dict, Generic, List, NoReturn, TypeVar


[docs]class ClassField: """ Representation of a data field. """ __slots__ = ('init_name', 'attr_name') def __init__(self, init_name: str, attr_name: str): # self.init_name = init_name super().__setattr__('init_name', init_name) # self.attr_name = attr_name super().__setattr__('attr_name', attr_name) def __hash__(self) -> int: return hash((self.init_name, self.attr_name)) def __setattr__(self, key, value) -> None: raise AttributeError def __eq__(self, other) -> bool: if isinstance(other, ClassField): return self.init_name == other.init_name and self.attr_name == other.attr_name return False @property def name(self) -> str: """ Name of the field (attribute name, may be different from the init name). """ return self.attr_name
[docs] def get_value(self, obj: Any) -> Any: """ Get value of object's field. :param obj: source object :return: value of field :: import attr class FieldedNT(NamedTuple): size: int color: str @attr.s class FieldedAttrs: size: int = attr.ib() color: str = attr.ib() field_size = ClassField(init_name='size', attr_name='size') nt_obj = FieldedNT(size=12, color='red') attrs_obj = FieldedAttrs(size=34, color='green') # via get_value method field_size.get_value(nt_obj) # == 12 # via __getitem__ field_size[attrs_obj] # == 34 """ try: return getattr(obj, self.attr_name) except AttributeError: if isinstance(obj, dict): return obj[self.attr_name] raise
[docs] def set_value(self, obj: Any, value: Any) -> None: """ Set value for object's field. :param obj: target object :param value: value for field :return: ``None`` :: import attr @attr.s class FieldedAttrs: size: int = attr.ib() color: str = attr.ib() field_size = ClassField(init_name='size', attr_name='size') field_color = ClassField(init_name='color', attr_name='color') attrs_obj = FieldedAttrs(size=56, color='green') # via get_value method field_size.set_value(attrs_obj, 78) # attrs_obj.size == 78 # via __getitem__ field_color[attrs_obj] = 'blue' # attrs_obj.color == 'blue' """ try: setattr(obj, self.attr_name, value) except AttributeError: if isinstance(obj, dict): obj[self.attr_name] = value raise
def __getitem__(self, obj: Any) -> Any: """ Alias for :func:`~classical.fields.base.ClassField.get_value` """ return self.get_value(obj=obj) def __setitem__(self, obj: Any, value: Any) -> None: """ Alias for :func:`~classical.fields.base.ClassField.set_value` """ self.set_value(obj=obj, value=value)
_ClassFieldType = TypeVar('_ClassFieldType', bound='ClassField')
[docs]class FieldSchema(List[_ClassFieldType]):
[docs] def get_field_dict(self, obj: Any) -> Dict[_ClassFieldType, Any]: result = {} # type: Dict[_ClassFieldType, Any] for field in self: result[field] = field[obj] return result
[docs] def set_field_dict(self, obj: Any, values: Dict[_ClassFieldType, Any]) -> None: for field in self: if field in values: field[obj] = values[field]
def __getitem__(self, obj: Any) -> Dict[_ClassFieldType, Any]: return self.get_field_dict(obj=obj) def __setitem__(self, obj: Any, values: Dict[_ClassFieldType, Any]) -> None: self.set_field_dict(obj=obj, values=values)
[docs]class FieldInspector(abc.ABC, Generic[_ClassFieldType]): @classmethod def _raise_unsupported_field_class(cls, insp_cls: type) -> NoReturn: raise TypeError( 'Unsupported class {insp_cls.__name__} for {cls.__name__}'.format( insp_cls=insp_cls, cls=cls, ) )
[docs] @classmethod def get_fields(cls, cls_or_obj: type) -> FieldSchema[_ClassFieldType]: """ Return list of fields for the given fielded class or object. :param cls_or_obj: class or instance to inspect. :return: list of :class:`~classical.fielded.base.ClassField` instances """ if isinstance(cls_or_obj, type): return cls._get_class_fields(cls_or_obj) return cls._get_instance_fields(cls_or_obj)
@classmethod @abc.abstractmethod def _get_class_fields(cls, insp_cls: type) -> FieldSchema[_ClassFieldType]: raise NotImplementedError @classmethod def _get_instance_fields(cls, obj: Any) -> FieldSchema[_ClassFieldType]: return cls._get_class_fields(type(obj))
[docs] @classmethod def get_field_dict(cls, obj: Any) -> Dict[_ClassFieldType, Any]: """ Return dict of ``obj``'s fields and their values :param obj: fielded instance :return: dict with :class:`~classical.fielded.base.ClassField` instances as keys """ field_schema = cls._get_instance_fields(obj) return field_schema[obj]
[docs] @classmethod def get_name_dict( cls, obj: Any, attr_mode: bool = False, init_mode: bool = False, ) -> Dict[str, Any]: """ Return dict of ``obj``'s field names and their values. :param obj: fielded instance :param attr_mode: use fields' ``attr_name`` as the dict's keys; mutually exclusive with ``init_mode`` :param init_mode: use fields' ``init_name`` as the dict's keys; mutually exclusive with ``attr_mode`` :return: dict with :class:`~classical.fielded.base.ClassField` instances as keys """ if attr_mode is False and init_mode is False: attr_mode = True if attr_mode is True and init_mode is True: raise ValueError('attr_mode and init_mode cannot both be True') result = {} # type: Dict[str, Any] for field in cls._get_instance_fields(obj): key = field.attr_name if attr_mode else field.init_name result[key] = field[obj] return result
[docs] @classmethod def get_name_list( cls, obj: Any, attr_mode: bool = False, init_mode: bool = False, ) -> List[str]: """ Return list of ``obj``'s field names. :param obj: fielded instance :param attr_mode: use fields' ``attr_name``; mutually exclusive with ``init_mode`` :param init_mode: use fields' ``init_name``; mutually exclusive with ``attr_mode`` :return: dict with :class:`~classical.fielded.base.ClassField` instances as keys """ if attr_mode is False and init_mode is False: attr_mode = True if attr_mode is True and init_mode is True: raise ValueError('attr_mode and init_mode cannot both be True') result = [] # type: List[str] for field in cls._get_instance_fields(obj): key = field.attr_name if attr_mode else field.init_name result.append(key) return result