Source code for minos.common.model.declarative

from __future__ import (
    annotations,
)

import logging
from functools import (
    lru_cache,
)
from itertools import (
    zip_longest,
)
from typing import (
    Any,
    Iterator,
    Optional,
    TypeVar,
    get_type_hints,
)

from ..meta import (
    self_or_classmethod,
)
from .abc import (
    Model,
)
from .types import (
    MissingSentinel,
    ModelType,
    TypeHintComparator,
)

logger = logging.getLogger(__name__)


[docs]class DeclarativeModel(Model): """Base class for ``minos`` declarative model entities."""
[docs] def __init__(self, *args, **kwargs): """Class constructor. :param kwargs: Named arguments to be set as model attributes. """ super().__init__() self._build_fields(*args, **kwargs)
# noinspection PyUnusedLocal
[docs] @classmethod def from_model_type(cls: type[T], model_type: ModelType, *args, **kwargs) -> T: """Build a ``DeclarativeModel`` from a ``ModelType``. :param model_type: ``ModelType`` object containing the model structure. :param args: Positional arguments to be passed to the model constructor. :param kwargs: Named arguments to be passed to the model constructor. :return: A new ``DeclarativeModel`` instance. """ return cls(*args, **kwargs)
def _build_fields(self, *args, additional_type_hints: Optional[dict[str, type]] = None, **kwargs) -> None: for (name, type_val), value in zip_longest( self._type_hints(additional_type_hints), args, fillvalue=MissingSentinel ): if name in kwargs and value is not MissingSentinel: raise TypeError(f"got multiple values for argument {repr(name)}") if value is MissingSentinel and name in kwargs: value = kwargs[name] self._fields[name] = self._field_cls( name, type_val, value, getattr(self, f"parse_{name}", None), getattr(self, f"validate_{name}", None) ) # noinspection PyMethodParameters @self_or_classmethod def _type_hints(self_or_cls, additional_type_hints: Optional[dict[str, type]] = None) -> Iterator[tuple[str, Any]]: type_hints = dict() if isinstance(self_or_cls, type): cls = self_or_cls else: cls = type(self_or_cls) for b in cls.__mro__[::-1]: list_fields = _get_class_type_hints(b) type_hints |= list_fields logger.debug(f"The obtained type hints are: {type_hints!r}") if additional_type_hints: for name, hint in additional_type_hints.items(): if name not in type_hints or TypeHintComparator(hint, type_hints[name]).match(): type_hints[name] = hint type_hints |= super()._type_hints() yield from type_hints.items()
@lru_cache() def _get_class_type_hints(b: type) -> dict[str, type]: return {k: v for k, v in get_type_hints(b).items() if not k.startswith("_")} T = TypeVar("T", bound=DeclarativeModel) MinosModel = DeclarativeModel