Source code for grave_settings.abstract

# - * -coding: utf - 8 - * -
"""


@author: ☙ Ryan McConnell ❧
"""
from abc import abstractmethod
from typing import TypeVar, MutableMapping, Type, Mapping, Callable, Self, Generator, Literal

from observer_hooks import notify

from grave_settings.conversion_manager import ConversionManager
from grave_settings.formatter_settings import FormatterContext, PreservedReference
from grave_settings.validation import SettingsValidator

_KT = TypeVar('_KT')
_VT = TypeVar('_VT')


[docs]class Serializable: __slots__ = '__weakref__',
[docs] @classmethod def check_in_serialization_context(cls, context: FormatterContext): """ This method typically is called on an instantiated object, so it may not be necessary for it to be a class method. """ pass
[docs] @classmethod def check_in_deserialization_context(cls, context: FormatterContext): """ This method is typically called on type objects, so it should remain a class method in sub-classes. This is where you can put a reference to :py:class:`~grave_settings.semantics.NotifyFinalizedMethodName` to get a callback """ # context.register_frame_semantic(NotifyFinalizedMethodName('finalize')) pass
[docs] def to_dict(self, context: FormatterContext, **kwargs) -> dict: zgen = ((i, getattr(self, i)) for i in dir(self)) return dict(i for i in zgen if not (callable(i[1]) or i[0].startswith('__')))
[docs] def from_dict(self, state_obj: dict, context: FormatterContext, **kwargs): for k, v in state_obj.items(): setattr(self, k, v)
[docs] def finalize(self, frame: FormatterContext) -> None: # This is pretty inefficient. Override it for key in dir(self): v = getattr(self, key) if isinstance(v, PreservedReference): setattr(self, key, frame.find(v))
[docs]class VersionedSerializable(Serializable): VERSION = None __slots__ = tuple()
[docs] @classmethod def get_version(cls): return cls.VERSION
[docs] @classmethod def get_version_object(cls): """ It is saf to override this as an instance method """ return cls.get_conversion_manager().get_version_object(cls)
[docs] @classmethod def get_conversion_manager(cls) -> ConversionManager: cm = ConversionManager() cm.converted.subscribe(cls.conversion_manager_converted) return cm
[docs] @classmethod def check_convert_update(cls, state_obj: dict, load_type: Callable[[str], Type], version_obj: dict) -> dict | Literal[False]: conversion_manager = cls.get_conversion_manager() rst = state_obj instance = conversion_manager.update_to_current(state_obj, load_type, version_obj) if rst is instance: return False else: return instance
[docs] @classmethod def conversion_manager_converted(cls, state_obj: dict, class_str: str, ver: str, target_ver: str = None): pass
[docs] @classmethod def get_versioning_endpoint(cls) -> Type[Self]: return VersionedSerializable
[docs]def make_kill_converter(cls: Type[VersionedSerializable]) -> Callable[[dict], dict]: return lambda _: cls().to_dict(explicit=True)
[docs]class IASettings(VersionedSerializable, MutableMapping): __slots__ = 'parent', '_invalidate', 'file_path' def __init__(self, *args, initialize_settings=True, **kwargs): self.parent: IASettings | None = None if initialize_settings: self.init_settings(**kwargs)
[docs] def init_settings(self, **kwargs) -> None: pass
[docs] def get_versioning_endpoint(self) -> Type[VersionedSerializable]: return IASettings
@notify() def invalidate(self) -> None: if self.parent is not None: self.parent.invalidate()
[docs] def update(self, mapping_obj: Mapping[_KT, _VT], **kwargs: _VT): it_t = type(mapping_obj) if it_t == list or it_t == tuple: for k, v in mapping_obj: self[k] = v else: for k, v in mapping_obj.items(): self[k] = v for k, v in kwargs.items(): self[k] = v
[docs] def finalize(self, frame: FormatterContext): for key, v in self.generate_key_value_pairs(): if isinstance(v, PreservedReference): self[key] = frame.find(v)
@abstractmethod def __contains__(self, item): pass @abstractmethod def __setitem__(self, key, value): pass @abstractmethod def __getitem__(self, item): pass @abstractmethod def __delitem__(self, itm): pass @abstractmethod def __iter__(self): pass @abstractmethod def __len__(self) -> int: pass def __bool__(self): # This is to avoid unexpected edge case bugs since __len__ is implemented. Maybe just use explicit checks return True
[docs] @abstractmethod def generate_key_value_pairs(self, **kwargs) -> Generator[tuple[object, object], None, None]: pass
[docs] def get_validator(self) -> SettingsValidator | None: pass
def __hash__(self): # override this if you want to support value based equality return hash(id(self))