Source code for zensols.config.nestedfac

"""A factory that can create nested objects.

"""
from __future__ import annotations
from typing import ClassVar, Any
from zensols.config import Settings, DictionaryConfig
from . import ImportConfigFactory


[docs] class NestedImportConfigFactory(ImportConfigFactory): """Creates nested objects that reflect nested instances of :class:`dict`. The class must be created with a :class:`~zensols.config.dictconfig.DictionaryConfig` with the dictionary that was used to create it. It uses it to create temporary objects that are then used as input to the super class to create the objects. In this way, we get the best of both worlds to create factory objects (i.e. ``instance:``) and nested classes. """ _OBJECT_TEMP_KEY: ClassVar[str] = '__obj' """Temporary object prefix to store."""
[docs] def __init__(self, config: DictionaryConfig, **kwargs): super().__init__(config=config, **kwargs) self._dconfig: dict[str, Any] = config.config self._is_nested = False
def _map_class_name(self, class_name: str) -> str: """Munge the class name for factories that want to use something other than the fully qualified class name. """ return class_name def _map_params(self, class_name: str, data: dict[str, Any]) -> \ dict[str, Any]: return data def _new_nested_instance(self, value: Any, depth: int, args: list = None, kwargs: dict = None) -> Any: depth += 1 if isinstance(value, Settings): value = value.asdict() if isinstance(value, dict): class_name: str = value.pop(self.CLASS_NAME, None) if class_name is not None: key: str = f'{self._OBJECT_TEMP_KEY}_{class_name}_{depth}' data: dict[str, Any] = dict(value) args = () if args is None else args if kwargs is not None: data.update(kwargs) data = dict(map( lambda t: (t[0], self._new_nested_instance(t[1], depth)), data.items())) self._dconfig[key] = data data = self._map_params(class_name, data) data[self.CLASS_NAME] = self._map_class_name(class_name) try: value = super().new_instance(key) finally: del self._dconfig[key] if depth > 0 and \ self.NAME_ATTRIBUTE not in data and \ hasattr(value, self.NAME_ATTRIBUTE): value.name = None else: value = dict(map( lambda t: (t[0], self._new_nested_instance(t[1], depth)), value.items())) elif isinstance(value, (tuple, list)): value = tuple(map( lambda v: self._new_nested_instance(v, depth), value)) return value
[docs] def instance(self, name: str | None, *args, **kwargs): inst: Any params: dict[str, Any] = self.config[name] if self._is_nested: inst = super().instance(name, *args, **kwargs) else: if self._shared is not None: inst = self._shared.get(name) if inst is None: if isinstance(params, Settings): params = params.asdict() self._is_nested = True try: inst = self._new_nested_instance(params, 0, args, kwargs) if hasattr(inst, self.NAME_ATTRIBUTE): setattr(inst, self.NAME_ATTRIBUTE, name) finally: self._is_nested = False if self._shared is not None: self._shared[name] = inst return inst