Source code for lories.core.configs.configurator

# -*- coding: utf-8 -*-
"""
lories.core.configs.configurator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


"""

from __future__ import annotations

import logging
from abc import ABCMeta
from collections import OrderedDict
from functools import wraps
from logging import Logger
from typing import Any, Dict, Optional

from lories._core import _Context, _Entity  # noqa
from lories._core._configurations import Configurations, _Configurations  # noqa
from lories._core._configurator import Configurator as ConfiguratorType  # noqa
from lories._core._configurator import _Configurator  # noqa
from lories.core.configs.errors import ConfigurationError
from lories.util import get_members


[docs] class ConfiguratorMeta(ABCMeta): def __call__(cls, *args, **kwargs): configurator = super().__call__(*args, **kwargs) cls._wrap_method(configurator, "duplicate") cls._wrap_method(configurator, "configure") cls._wrap_method(configurator, "update") return configurator # noinspection PyShadowingBuiltins @staticmethod def _wrap_method(object: Any, method: str) -> None: _wrap_method = getattr(object, f"_do_{method}") _run_method = getattr(object, method) setattr(object, f"_run_{method}", _run_method) setattr(object, method, _wrap_method)
[docs] class Configurator(_Configurator, metaclass=ConfiguratorMeta): __configs: _Configurations _configured: bool = False _logger: Logger def __init__( self, configs: Optional[Configurations] = None, logger: Optional[Logger] = None, **kwargs, ) -> None: super().__init__(**kwargs) if logger is None: logger = self._build_logger() self._logger = logger self.__configs = self._assert_configs(configs) def __eq__(self, other: Any) -> bool: return self is other def __hash__(self) -> int: return hash(id(self)) def __copy__(self) -> ConfiguratorType: return self.copy() def __replace__(self, **changes) -> ConfiguratorType: return self.duplicate(**changes) @classmethod def _assert_configs(cls, configs: Optional[Configurations]) -> Optional[Configurations]: if configs is None: return None if not isinstance(configs, _Configurations): raise ConfigurationError(f"Invalid '{cls.__name__}' configurations: {type(configs)}") return configs @classmethod def _build_logger(cls) -> Logger: return logging.getLogger(cls.__module__) def _get_vars(self) -> Dict[str, Any]: def _is_var(attr: str, var: Any) -> bool: return not ( attr.startswith("_") or attr.isupper() or callable(var) or isinstance(var, _Context) or isinstance(var, _Configurations) ) return get_members(self, filter=_is_var) # noinspection PyShadowingBuiltins def _convert_vars(self, convert: callable = str) -> Dict[str, str]: def _convert(var: Any) -> str: return str(var) if not isinstance(var, (_Context, _Configurator, _Entity)) else convert(var) vars = self._get_vars() values = OrderedDict([(k, _convert(v)) for k, v in vars.items()]) if self.configs is not None: values["enabled"] = str(self.is_enabled()) values["configured"] = str(self.is_configured()) values["configs"] = convert(self.configs) return values # noinspection PyShadowingBuiltins def __repr__(self) -> str: vars = [f"{k}={v}" for k, v in self._convert_vars(lambda v: f"<{type(v).__name__}>").items()] return f"{type(self).__name__}({', '.join(vars)})" # noinspection PyShadowingBuiltins def __str__(self) -> str: vars = [f"{k} = {v}" for k, v in self._convert_vars(repr).items()] return f"{type(self).__name__}:\n\t" + "\n\t".join(vars) def is_enabled(self) -> bool: return self.__configs is not None and self.__configs.enabled def is_configured(self) -> bool: return self._configured @property def configs(self) -> Optional[Configurations]: return self.__configs # noinspection PyUnresolvedReferences @wraps(_Configurator.configure, updated=()) def _do_configure(self, configs: Configurations, *args, **kwargs) -> None: if configs is None: raise ConfigurationError(f"Invalid NoneType configuration for {type(self).__name__}: {self.name}") if not configs.enabled: raise ConfigurationError(f"Trying to configure disabled {type(self).__name__}: {configs.name}") if self.is_configured(): self._logger.warning(f"{type(self).__name__} '{configs.path}' already configured") return self._assert_configs(configs) self._at_configure(configs) self._run_configure(configs, *args, **kwargs) self._on_configure(configs) self.__configs = configs self._configured = True def _at_configure(self, configs: Configurations) -> None: pass def _on_configure(self, configs: Configurations) -> None: pass # noinspection PyUnresolvedReferences def update(self, configs: Configurations) -> None: self._run_configure(configs) # noinspection PyUnresolvedReferences @wraps(_Configurator.update, updated=()) def _do_update(self, configs: Configurations, *args, **kwargs) -> None: if configs is None: raise ConfigurationError(f"Invalid NoneType configuration for {type(self).__name__}: {self.name}") if not configs.enabled: raise ConfigurationError(f"Trying to update disabled {type(self).__name__}: {configs.name}") if not self.is_configured(): self._logger.warning(f"Trying to update unconfigured {type(self).__name__}: '{configs.path}'") return self._assert_configs(configs) self._at_update(configs) self._run_update(configs, *args, **kwargs) self._on_update(configs) self.__configs = configs def _at_update(self, configs: Configurations) -> None: pass def _on_update(self, configs: Configurations) -> None: pass # noinspection PyUnresolvedReferences, PyTypeChecker def copy(self) -> ConfiguratorType: try: copier = super().copy except AttributeError: copier = self.duplicate return copier() # noinspection PyUnresolvedReferences def duplicate(self, configs: Optional[Configurations] = None, **changes) -> ConfiguratorType: if configs is None: configs = self.__configs.copy() try: duplicator = super().duplicate except AttributeError: duplicator = type(self) return duplicator(configs=configs, **changes) # noinspection PyUnresolvedReferences @wraps(_Configurator.duplicate, updated=()) def _do_duplicate(self, configs: Configurations, **changes) -> ConfiguratorType: if configs is None: configs = self.__configs.copy() self._at_duplicate(configs=configs, **changes) duplicate = self._run_duplicate(configs=configs, **changes) if configs.enabled: duplicate.configure(configs) self._on_duplicate(duplicate) return duplicate def _at_duplicate(self, **changes) -> None: pass def _on_duplicate(self, configurator: Configurator) -> None: pass