from __future__ import (
annotations,
)
import logging
from itertools import (
zip_longest,
)
from typing import (
Iterable,
Type,
TypeVar,
Union,
)
from ..abc import (
Model,
)
from ..fields import (
Field,
)
from ..types import (
MissingSentinel,
ModelType,
)
logger = logging.getLogger(__name__)
[docs]class DynamicModel(Model):
"""Base class for ``minos`` dynamic model entities"""
[docs] def __init__(self, fields: Union[Iterable[Field], dict[str, Field]], **kwargs):
super().__init__(fields)
[docs] @classmethod
def from_model_type(cls: Type[T], model_type: ModelType, *args, **kwargs) -> T:
"""Build a ``DynamicModel`` 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 ``DynamicModel`` instance.
"""
fields = cls._build_fields(model_type.type_hints, *args, **kwargs)
return cls(fields=fields)
@classmethod
def _build_fields(cls, type_hints: dict[str, type], *args, **kwargs) -> dict[str, Field]:
fields = dict()
for (name, type_val), value in zip_longest(type_hints.items(), 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]
fields[name] = cls._field_cls(name, type_val, value)
return fields
T = TypeVar("T", bound=DynamicModel)