| from __future__ import annotations |
|
|
| import datetime |
| import decimal |
| import enum |
| import uuid |
| from collections.abc import Iterable |
| from typing import ( |
| Any, |
| Final, |
| Literal, |
| Tuple, |
| Type as typing_Type, |
| TypeVar, |
| Union, |
| ) |
|
|
| try: |
| from types import UnionType as _types_UnionType |
| except Exception: |
| _types_UnionType = type("UnionType", (), {}) |
|
|
| try: |
| from typing import TypeAliasType as _TypeAliasType |
| except Exception: |
| _TypeAliasType = type("TypeAliasType", (), {}) |
|
|
| import msgspec |
| from msgspec import NODEFAULT, UNSET, UnsetType as _UnsetType |
|
|
| from ._core import ( |
| Factory as _Factory, |
| to_builtins as _to_builtins, |
| ) |
| from ._utils import ( |
| _CONCRETE_TYPES, |
| _AnnotatedAlias, |
| get_class_annotations as _get_class_annotations, |
| get_dataclass_info as _get_dataclass_info, |
| get_typeddict_info as _get_typeddict_info, |
| ) |
|
|
| __all__ = ( |
| "type_info", |
| "multi_type_info", |
| "Type", |
| "Metadata", |
| "AnyType", |
| "NoneType", |
| "BoolType", |
| "IntType", |
| "FloatType", |
| "StrType", |
| "BytesType", |
| "ByteArrayType", |
| "MemoryViewType", |
| "DateTimeType", |
| "TimeType", |
| "DateType", |
| "TimeDeltaType", |
| "UUIDType", |
| "DecimalType", |
| "ExtType", |
| "RawType", |
| "EnumType", |
| "LiteralType", |
| "CustomType", |
| "UnionType", |
| "CollectionType", |
| "ListType", |
| "SetType", |
| "FrozenSetType", |
| "VarTupleType", |
| "TupleType", |
| "DictType", |
| "Field", |
| "TypedDictType", |
| "NamedTupleType", |
| "DataclassType", |
| "StructType", |
| ) |
|
|
|
|
| def __dir__(): |
| return __all__ |
|
|
|
|
| class Type(msgspec.Struct): |
| """The base Type.""" |
|
|
|
|
| class Metadata(Type): |
| """A type wrapping a subtype with additional metadata. |
| |
| Parameters |
| ---------- |
| type: Type |
| The subtype. |
| extra_json_schema: dict, optional |
| A dict of extra fields to set for the subtype when generating a |
| json-schema. |
| extra: dict, optional |
| A dict of extra user-defined metadata attached to the subtype. |
| """ |
|
|
| type: Type |
| extra_json_schema: Union[dict, None] = None |
| extra: Union[dict, None] = None |
|
|
|
|
| class AnyType(Type): |
| """A type corresponding to `typing.Any`.""" |
|
|
|
|
| class NoneType(Type): |
| """A type corresponding to `None`.""" |
|
|
|
|
| class BoolType(Type): |
| """A type corresponding to `bool`.""" |
|
|
|
|
| class IntType(Type): |
| """A type corresponding to `int`. |
| |
| Parameters |
| ---------- |
| gt: int, optional |
| If set, an instance of this type must be greater than ``gt``. |
| ge: int, optional |
| If set, an instance of this type must be greater than or equal to ``ge``. |
| lt: int, optional |
| If set, an instance of this type must be less than to ``lt``. |
| le: int, optional |
| If set, an instance of this type must be less than or equal to ``le``. |
| multiple_of: int, optional |
| If set, an instance of this type must be a multiple of ``multiple_of``. |
| """ |
|
|
| gt: Union[int, None] = None |
| ge: Union[int, None] = None |
| lt: Union[int, None] = None |
| le: Union[int, None] = None |
| multiple_of: Union[int, None] = None |
|
|
|
|
| class FloatType(Type): |
| """A type corresponding to `float`. |
| |
| Parameters |
| ---------- |
| gt: float, optional |
| If set, an instance of this type must be greater than ``gt``. |
| ge: float, optional |
| If set, an instance of this type must be greater than or equal to ``ge``. |
| lt: float, optional |
| If set, an instance of this type must be less than to ``lt``. |
| le: float, optional |
| If set, an instance of this type must be less than or equal to ``le``. |
| multiple_of: float, optional |
| If set, an instance of this type must be a multiple of ``multiple_of``. |
| """ |
|
|
| gt: Union[float, None] = None |
| ge: Union[float, None] = None |
| lt: Union[float, None] = None |
| le: Union[float, None] = None |
| multiple_of: Union[float, None] = None |
|
|
|
|
| class StrType(Type): |
| """A type corresponding to `str`. |
| |
| Parameters |
| ---------- |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| pattern: str, optional |
| If set, an instance of this type must match against this regex pattern. |
| Note that the pattern is treated as **unanchored**. |
| """ |
|
|
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
| pattern: Union[str, None] = None |
|
|
|
|
| class BytesType(Type): |
| """A type corresponding to `bytes`. |
| |
| Parameters |
| ---------- |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
|
|
|
|
| class ByteArrayType(Type): |
| """A type corresponding to `bytearray`. |
| |
| Parameters |
| ---------- |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
|
|
|
|
| class MemoryViewType(Type): |
| """A type corresponding to `memoryview`. |
| |
| Parameters |
| ---------- |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
|
|
|
|
| class DateTimeType(Type): |
| """A type corresponding to `datetime.datetime`. |
| |
| Parameters |
| ---------- |
| tz: bool |
| The timezone-requirements for an instance of this type. ``True`` |
| indicates a timezone-aware value is required, ``False`` indicates a |
| timezone-aware value is required. The default is ``None``, which |
| accepts either timezone-aware or timezone-naive values. |
| """ |
|
|
| tz: Union[bool, None] = None |
|
|
|
|
| class TimeType(Type): |
| """A type corresponding to `datetime.time`. |
| |
| Parameters |
| ---------- |
| tz: bool |
| The timezone-requirements for an instance of this type. ``True`` |
| indicates a timezone-aware value is required, ``False`` indicates a |
| timezone-aware value is required. The default is ``None``, which |
| accepts either timezone-aware or timezone-naive values. |
| """ |
|
|
| tz: Union[bool, None] = None |
|
|
|
|
| class DateType(Type): |
| """A type corresponding to `datetime.date`.""" |
|
|
|
|
| class TimeDeltaType(Type): |
| """A type corresponding to `datetime.timedelta`.""" |
|
|
|
|
| class UUIDType(Type): |
| """A type corresponding to `uuid.UUID`.""" |
|
|
|
|
| class DecimalType(Type): |
| """A type corresponding to `decimal.Decimal`.""" |
|
|
|
|
| class ExtType(Type): |
| """A type corresponding to `msgspec.msgpack.Ext`.""" |
|
|
|
|
| class RawType(Type): |
| """A type corresponding to `msgspec.Raw`.""" |
|
|
|
|
| class EnumType(Type): |
| """A type corresponding to an `enum.Enum` type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding `enum.Enum` type. |
| """ |
|
|
| cls: typing_Type[enum.Enum] |
|
|
|
|
| class LiteralType(Type): |
| """A type corresponding to a `typing.Literal` type. |
| |
| Parameters |
| ---------- |
| values: tuple |
| A tuple of possible values for this literal instance. Only `str` or |
| `int` literals are supported. |
| """ |
|
|
| values: Union[Tuple[str, ...], Tuple[int, ...]] |
|
|
|
|
| class CustomType(Type): |
| """A custom type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding custom type. |
| """ |
|
|
| cls: type |
|
|
|
|
| class UnionType(Type): |
| """A union type. |
| |
| Parameters |
| ---------- |
| types: Tuple[Type, ...] |
| A tuple of possible types for this union. |
| """ |
|
|
| types: Tuple[Type, ...] |
|
|
| @property |
| def includes_none(self) -> bool: |
| """A helper for checking whether ``None`` is included in this union.""" |
| return any(isinstance(t, NoneType) for t in self.types) |
|
|
|
|
| class CollectionType(Type): |
| """A collection type. |
| |
| This is the base type shared by collection types like `ListType`, |
| `SetType`, etc. |
| |
| Parameters |
| ---------- |
| item_type: Type |
| The item type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
| item_type: Type |
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
|
|
|
|
| class ListType(CollectionType): |
| """A type corresponding to a `list`. |
| |
| Parameters |
| ---------- |
| item_type: Type |
| The item type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
|
|
| class VarTupleType(CollectionType): |
| """A type corresponding to a variadic `tuple`. |
| |
| Parameters |
| ---------- |
| item_type: Type |
| The item type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
|
|
| class SetType(CollectionType): |
| """A type corresponding to a `set`. |
| |
| Parameters |
| ---------- |
| item_type: Type |
| The item type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
|
|
| class FrozenSetType(CollectionType): |
| """A type corresponding to a `frozenset`. |
| |
| Parameters |
| ---------- |
| item_type: Type |
| The item type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
|
|
| class TupleType(Type): |
| """A type corresponding to `tuple`. |
| |
| Parameters |
| ---------- |
| item_types: Tuple[Type, ...] |
| A tuple of types for each element in the tuple. |
| """ |
|
|
| item_types: Tuple[Type, ...] |
|
|
|
|
| class DictType(Type): |
| """A type corresponding to `dict`. |
| |
| Parameters |
| ---------- |
| key_type: Type |
| The key type. |
| value_type: Type |
| The value type. |
| min_length: int, optional |
| If set, an instance of this type must have length greater than or equal |
| to ``min_length``. |
| max_length: int, optional |
| If set, an instance of this type must have length less than or equal |
| to ``max_length``. |
| """ |
|
|
| key_type: Type |
| value_type: Type |
| min_length: Union[int, None] = None |
| max_length: Union[int, None] = None |
|
|
|
|
| class Field(msgspec.Struct): |
| """A record describing a field in an object-like type. |
| |
| Parameters |
| ---------- |
| name: str |
| The field name as seen by Python code (e.g. ``field_one``). |
| encode_name: str |
| The name used when encoding/decoding the field. This may differ if |
| the field is renamed (e.g. ``fieldOne``). |
| type: Type |
| The field type. |
| required: bool, optional |
| Whether the field is required. Note that if `required` is False doesn't |
| necessarily mean that `default` or `default_factory` will be set - |
| optional fields may exist with no default value. |
| default: Any, optional |
| A default value for the field. Will be `NODEFAULT` if no default value |
| is set. |
| default_factory: Any, optional |
| A callable that creates a default value for the field. Will be |
| `NODEFAULT` if no ``default_factory`` is set. |
| """ |
|
|
| name: str |
| encode_name: str |
| type: Type |
| required: bool = True |
| default: Any = msgspec.field(default_factory=lambda: NODEFAULT) |
| default_factory: Any = msgspec.field(default_factory=lambda: NODEFAULT) |
|
|
|
|
| class TypedDictType(Type): |
| """A type corresponding to a `typing.TypedDict` type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding TypedDict type. |
| fields: Tuple[Field, ...] |
| A tuple of fields in the TypedDict. |
| """ |
|
|
| cls: type |
| fields: Tuple[Field, ...] |
|
|
|
|
| class NamedTupleType(Type): |
| """A type corresponding to a `typing.NamedTuple` type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding NamedTuple type. |
| fields: Tuple[Field, ...] |
| A tuple of fields in the NamedTuple. |
| """ |
|
|
| cls: type |
| fields: Tuple[Field, ...] |
|
|
|
|
| class DataclassType(Type): |
| """A type corresponding to a `dataclasses` or `attrs` type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding dataclass type. |
| fields: Tuple[Field, ...] |
| A tuple of fields in the dataclass. |
| """ |
|
|
| cls: type |
| fields: Tuple[Field, ...] |
|
|
|
|
| class StructType(Type): |
| """A type corresponding to a `msgspec.Struct` type. |
| |
| Parameters |
| ---------- |
| cls: type |
| The corresponding Struct type. |
| fields: Tuple[Field, ...] |
| A tuple of fields in the Struct. |
| tag_field: str or None, optional |
| If set, the field name used for the tag in a tagged union. |
| tag: str, int, or None, optional |
| If set, the value used for the tag in a tagged union. |
| array_like: bool, optional |
| Whether the struct is encoded as an array rather than an object. |
| forbid_unknown_fields: bool, optional |
| If ``False`` (the default) unknown fields are ignored when decoding. If |
| ``True`` any unknown fields will result in an error. |
| """ |
|
|
| cls: typing_Type[msgspec.Struct] |
| fields: Tuple[Field, ...] |
| tag_field: Union[str, None] = None |
| tag: Union[str, int, None] = None |
| array_like: bool = False |
| forbid_unknown_fields: bool = False |
|
|
|
|
| def multi_type_info(types: Iterable[Any]) -> tuple[Type, ...]: |
| """Get information about multiple msgspec-compatible types. |
| |
| Parameters |
| ---------- |
| types: an iterable of types |
| The types to get info about. |
| |
| Returns |
| ------- |
| tuple[Type, ...] |
| |
| Examples |
| -------- |
| >>> msgspec.inspect.multi_type_info([int, float, list[str]]) # doctest: +NORMALIZE_WHITESPACE |
| (IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None), |
| FloatType(gt=None, ge=None, lt=None, le=None, multiple_of=None), |
| ListType(item_type=StrType(min_length=None, max_length=None, pattern=None), |
| min_length=None, max_length=None)) |
| """ |
| return _Translator(types).run() |
|
|
|
|
| def type_info(type: Any) -> Type: |
| """Get information about a msgspec-compatible type. |
| |
| Note that if you need to inspect multiple types it's more efficient to call |
| `multi_type_info` once with a sequence of types than calling `type_info` |
| multiple times. |
| |
| Parameters |
| ---------- |
| type: type |
| The type to get info about. |
| |
| Returns |
| ------- |
| Type |
| |
| Examples |
| -------- |
| >>> msgspec.inspect.type_info(bool) |
| BoolType() |
| |
| >>> msgspec.inspect.type_info(int) |
| IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None) |
| |
| >>> msgspec.inspect.type_info(list[int]) # doctest: +NORMALIZE_WHITESPACE |
| ListType(item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None), |
| min_length=None, max_length=None) |
| """ |
| return multi_type_info([type])[0] |
|
|
|
|
| |
| def _origin_args_metadata(t): |
| |
| metadata = [] |
| while True: |
| try: |
| origin = _CONCRETE_TYPES.get(t) |
| except TypeError: |
| |
| origin = None |
|
|
| if origin is not None: |
| args = None |
| break |
|
|
| origin = getattr(t, "__origin__", None) |
| if origin is not None: |
| if type(t) is _AnnotatedAlias: |
| metadata.extend(m for m in t.__metadata__ if type(m) is msgspec.Meta) |
| t = origin |
| elif origin == Final: |
| t = t.__args__[0] |
| elif type(origin) is _TypeAliasType: |
| t = origin.__value__[t.__args__] |
| else: |
| args = getattr(t, "__args__", None) |
| origin = _CONCRETE_TYPES.get(origin, origin) |
| break |
| else: |
| supertype = getattr(t, "__supertype__", None) |
| if supertype is not None: |
| t = supertype |
| elif type(t) is _TypeAliasType: |
| t = t.__value__ |
| else: |
| origin = t |
| args = None |
| break |
|
|
| if type(origin) is _types_UnionType: |
| args = origin.__args__ |
| origin = Union |
| return origin, args, tuple(metadata) |
|
|
|
|
| def _is_struct(t): |
| return type(t) is type(msgspec.Struct) |
|
|
|
|
| def _is_enum(t): |
| return type(t) is enum.EnumMeta |
|
|
|
|
| def _is_dataclass(t): |
| return hasattr(t, "__dataclass_fields__") |
|
|
|
|
| def _is_attrs(t): |
| return hasattr(t, "__attrs_attrs__") |
|
|
|
|
| def _is_typeddict(t): |
| try: |
| return issubclass(t, dict) and hasattr(t, "__total__") |
| except TypeError: |
| return False |
|
|
|
|
| def _is_namedtuple(t): |
| try: |
| return issubclass(t, tuple) and hasattr(t, "_fields") |
| except TypeError: |
| return False |
|
|
|
|
| def _merge_json(a, b): |
| if b: |
| a = a.copy() |
| for key, b_val in b.items(): |
| if key in a: |
| a_val = a[key] |
| if isinstance(a_val, dict) and isinstance(b_val, dict): |
| a[key] = _merge_json(a_val, b_val) |
| elif isinstance(a_val, (list, tuple)) and isinstance( |
| b_val, (list, tuple) |
| ): |
| a[key] = list(a_val) + list(b_val) |
| else: |
| a[key] = b_val |
| else: |
| a[key] = b_val |
| return a |
|
|
|
|
| class _Translator: |
| def __init__(self, types): |
| self.types = tuple(types) |
| self.type_hints = {} |
| self.cache = {} |
|
|
| def _get_class_annotations(self, t): |
| """A cached version of `get_class_annotations`""" |
| try: |
| return self.type_hints[t] |
| except KeyError: |
| out = self.type_hints[t] = _get_class_annotations(t) |
| return out |
|
|
| def run(self): |
| |
| from ._core import MsgpackDecoder |
|
|
| MsgpackDecoder(Tuple[self.types]) |
| return tuple(self.translate(t) for t in self.types) |
|
|
| def translate(self, typ): |
| t, args, metadata = _origin_args_metadata(typ) |
|
|
| |
| constrs = {} |
| extra_json_schema = {} |
| extra = {} |
| for meta in metadata: |
| for attr in ( |
| "ge", |
| "gt", |
| "le", |
| "lt", |
| "multiple_of", |
| "pattern", |
| "min_length", |
| "max_length", |
| "tz", |
| ): |
| if (val := getattr(meta, attr)) is not None: |
| constrs[attr] = val |
| for attr in ("title", "description", "examples"): |
| if (val := getattr(meta, attr)) is not None: |
| extra_json_schema[attr] = val |
| if meta.extra_json_schema is not None: |
| extra_json_schema = _merge_json( |
| extra_json_schema, |
| _to_builtins(meta.extra_json_schema, str_keys=True), |
| ) |
| if meta.extra is not None: |
| extra.update(meta.extra) |
|
|
| out = self._translate_inner(t, args, **constrs) |
| if extra_json_schema or extra: |
| |
| |
| return Metadata( |
| out, extra_json_schema=extra_json_schema or None, extra=extra or None |
| ) |
| return out |
|
|
| def _translate_inner( |
| self, |
| t, |
| args, |
| ge=None, |
| gt=None, |
| le=None, |
| lt=None, |
| multiple_of=None, |
| pattern=None, |
| min_length=None, |
| max_length=None, |
| tz=None, |
| ): |
| if t is Any: |
| return AnyType() |
| elif isinstance(t, TypeVar): |
| if t.__bound__ is not None: |
| return self.translate(t.__bound__) |
| return AnyType() |
| elif t is None or t is type(None): |
| return NoneType() |
| elif t is bool: |
| return BoolType() |
| elif t is int: |
| return IntType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of) |
| elif t is float: |
| return FloatType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of) |
| elif t is str: |
| return StrType( |
| min_length=min_length, max_length=max_length, pattern=pattern |
| ) |
| elif t is bytes: |
| return BytesType(min_length=min_length, max_length=max_length) |
| elif t is bytearray: |
| return ByteArrayType(min_length=min_length, max_length=max_length) |
| elif t is memoryview: |
| return MemoryViewType(min_length=min_length, max_length=max_length) |
| elif t is datetime.datetime: |
| return DateTimeType(tz=tz) |
| elif t is datetime.time: |
| return TimeType(tz=tz) |
| elif t is datetime.date: |
| return DateType() |
| elif t is datetime.timedelta: |
| return TimeDeltaType() |
| elif t is uuid.UUID: |
| return UUIDType() |
| elif t is decimal.Decimal: |
| return DecimalType() |
| elif t is msgspec.Raw: |
| return RawType() |
| elif t is msgspec.msgpack.Ext: |
| return ExtType() |
| elif t is list: |
| return ListType( |
| self.translate(args[0]) if args else AnyType(), |
| min_length=min_length, |
| max_length=max_length, |
| ) |
| elif t is set: |
| return SetType( |
| self.translate(args[0]) if args else AnyType(), |
| min_length=min_length, |
| max_length=max_length, |
| ) |
| elif t is frozenset: |
| return FrozenSetType( |
| self.translate(args[0]) if args else AnyType(), |
| min_length=min_length, |
| max_length=max_length, |
| ) |
| elif t is tuple: |
| |
| |
| |
| if args == ((),): |
| args = () |
| if args is None: |
| return VarTupleType( |
| AnyType(), min_length=min_length, max_length=max_length |
| ) |
| elif len(args) == 2 and args[-1] is ...: |
| return VarTupleType( |
| self.translate(args[0]), |
| min_length=min_length, |
| max_length=max_length, |
| ) |
| else: |
| return TupleType(tuple(self.translate(a) for a in args)) |
| elif t is dict: |
| return DictType( |
| self.translate(args[0]) if args else AnyType(), |
| self.translate(args[1]) if args else AnyType(), |
| min_length=min_length, |
| max_length=max_length, |
| ) |
| elif t is Union: |
| args = tuple(self.translate(a) for a in args if a is not _UnsetType) |
| return args[0] if len(args) == 1 else UnionType(args) |
| elif t is Literal: |
| return LiteralType(tuple(sorted(args))) |
| elif _is_enum(t): |
| return EnumType(t) |
| elif _is_struct(t): |
| cls = t[args] if args else t |
| if cls in self.cache: |
| return self.cache[cls] |
| config = t.__struct_config__ |
| self.cache[cls] = out = StructType( |
| cls, |
| (), |
| tag_field=config.tag_field, |
| tag=config.tag, |
| array_like=config.array_like, |
| forbid_unknown_fields=config.forbid_unknown_fields, |
| ) |
|
|
| hints = self._get_class_annotations(cls) |
| npos = len(t.__struct_fields__) - len(t.__struct_defaults__) |
| fields = [] |
| for name, encode_name, default_obj in zip( |
| t.__struct_fields__, |
| t.__struct_encode_fields__, |
| (NODEFAULT,) * npos + t.__struct_defaults__, |
| ): |
| if default_obj is NODEFAULT: |
| required = True |
| default = default_factory = NODEFAULT |
| elif isinstance(default_obj, _Factory): |
| required = False |
| default = NODEFAULT |
| default_factory = default_obj.factory |
| else: |
| required = False |
| default = NODEFAULT if default_obj is UNSET else default_obj |
| default_factory = NODEFAULT |
|
|
| field = Field( |
| name=name, |
| encode_name=encode_name, |
| type=self.translate(hints[name]), |
| required=required, |
| default=default, |
| default_factory=default_factory, |
| ) |
| fields.append(field) |
|
|
| out.fields = tuple(fields) |
| return out |
| elif _is_typeddict(t): |
| cls = t[args] if args else t |
| if cls in self.cache: |
| return self.cache[cls] |
| self.cache[cls] = out = TypedDictType(cls, ()) |
| hints, required = _get_typeddict_info(cls) |
| out.fields = tuple( |
| Field( |
| name=name, |
| encode_name=name, |
| type=self.translate(field_type), |
| required=name in required, |
| ) |
| for name, field_type in sorted(hints.items()) |
| ) |
| return out |
| elif _is_dataclass(t) or _is_attrs(t): |
| cls = t[args] if args else t |
| if cls in self.cache: |
| return self.cache[cls] |
| self.cache[cls] = out = DataclassType(cls, ()) |
| _, info, defaults, _, _ = _get_dataclass_info(cls) |
| defaults = ((NODEFAULT,) * (len(info) - len(defaults))) + defaults |
| fields = [] |
| for (name, typ, is_factory), default_obj in zip(info, defaults): |
| if default_obj is NODEFAULT: |
| required = True |
| default = default_factory = NODEFAULT |
| elif is_factory: |
| required = False |
| default = NODEFAULT |
| default_factory = default_obj |
| else: |
| required = False |
| default = NODEFAULT if default_obj is UNSET else default_obj |
| default_factory = NODEFAULT |
|
|
| fields.append( |
| Field( |
| name=name, |
| encode_name=name, |
| type=self.translate(typ), |
| required=required, |
| default=default, |
| default_factory=default_factory, |
| ) |
| ) |
| out.fields = tuple(fields) |
| return out |
| elif _is_namedtuple(t): |
| cls = t[args] if args else t |
| if cls in self.cache: |
| return self.cache[cls] |
| self.cache[cls] = out = NamedTupleType(cls, ()) |
| hints = self._get_class_annotations(cls) |
| out.fields = tuple( |
| Field( |
| name=name, |
| encode_name=name, |
| type=self.translate(hints.get(name, Any)), |
| required=name not in t._field_defaults, |
| default=t._field_defaults.get(name, NODEFAULT), |
| ) |
| for name in t._fields |
| ) |
| return out |
| else: |
| return CustomType(t) |
|
|