Source code for minos.common.model.types.builders

from __future__ import (
    annotations,
)

import logging
from collections.abc import (
    Iterable,
)
from functools import (
    lru_cache,
)
from typing import (
    Any,
    Optional,
    Union,
    get_args,
    get_origin,
)

from .comparators import (
    TypeHintComparator,
    is_model_subclass,
    is_model_type,
)
from .model_types import (
    ModelType,
)

logger = logging.getLogger(__name__)


[docs]def build_union(options: Iterable[type, ...]) -> type: """Build the union type base on the given options. :param options: A tuple of types. :return: The union of types. """ return Union[tuple(options)]
[docs]class TypeHintBuilder: """Type Hint Builder class."""
[docs] def __init__(self, value: Any, type_: Optional[type] = None): self.value = value self.type_ = type_
[docs] def build(self) -> type: """Build type hint from an instance.. :return: A type. """ return self._build(self.value, self.type_)
def _build(self, value, type_: Optional[type]) -> type: if type_ is not None: if get_origin(type_) is Union: dynamic = self._build(value, None) options = tuple(self._build_from_dynamic(dynamic, static) for static in get_args(type_)) return build_union(options) if isinstance(value, (tuple, list, set)): b1 = Any if (type_ is None or len(get_args(type_)) != 1) else get_args(type_)[0] return type(value)[self._build_from_iterable(value, b1)] if isinstance(value, dict): b1, b2 = (str, Any) if (type_ is None or len(get_args(type_)) != 2) else get_args(type_) return type(value)[ self._build_from_iterable(value.keys(), b1), self._build_from_iterable(value.values(), b2) ] if is_model_type(value): return ModelType.from_model(value) if type_ is not None: dynamic = self._build(value, None) return self._build_from_dynamic(dynamic, type_) return type(value) def _build_from_iterable(self, values: Iterable, type_: Optional[type]) -> type: values = tuple(values) if len(values) == 0: return type_ options = tuple(self._build(value, type_) for value in values) return build_union(options) @staticmethod def _build_from_dynamic(dynamic: type, static: Optional[type]) -> type: return dynamic if not len(get_args(static)) and TypeHintComparator(dynamic, static).match() else static
[docs]class TypeHintParser: """Type Hint Parser class."""
[docs] def __init__(self, type_: Optional[type] = None): self.type_ = type_
[docs] def build(self) -> type: """Parse type hint. :return: A type. """ return self._build(self.type_)
@classmethod @lru_cache() def _build(cls, type_: Optional[type]) -> type: if is_model_subclass(type_): # noinspection PyTypeChecker return ModelType.from_model(type_) origin = get_origin(type_) if origin is None: return type_ args = get_args(type_) return cls._build(origin)[tuple(cls._build(arg) for arg in args)]