Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/msgspec/inspect.py: 4%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import datetime
4import decimal
5import enum
6import sys
7import uuid
8from collections.abc import Iterable
9from types import UnionType as _types_UnionType
10from typing import Any, Final, Literal, TypeVar, Union
12try:
13 from typing import TypeAliasType as _TypeAliasType # type: ignore
14except Exception:
15 _TypeAliasType = type("TypeAliasType", (), {}) # type: ignore
17import msgspec
18from msgspec import NODEFAULT, UNSET, UnsetType as _UnsetType
20from ._core import ( # type: ignore
21 Factory as _Factory,
22 to_builtins as _to_builtins,
23)
24from ._typing_utils import is_struct, is_struct_type
25from ._utils import ( # type: ignore
26 _CONCRETE_TYPES,
27 _AnnotatedAlias,
28 get_class_annotations as _get_class_annotations,
29 get_dataclass_info as _get_dataclass_info,
30 get_typeddict_info as _get_typeddict_info,
31)
33__all__ = (
34 "type_info",
35 "multi_type_info",
36 "Type",
37 "Metadata",
38 "AnyType",
39 "NoneType",
40 "BoolType",
41 "IntType",
42 "FloatType",
43 "StrType",
44 "BytesType",
45 "ByteArrayType",
46 "MemoryViewType",
47 "DateTimeType",
48 "TimeType",
49 "DateType",
50 "TimeDeltaType",
51 "UUIDType",
52 "DecimalType",
53 "ExtType",
54 "RawType",
55 "EnumType",
56 "LiteralType",
57 "CustomType",
58 "UnionType",
59 "CollectionType",
60 "ListType",
61 "SetType",
62 "FrozenSetType",
63 "VarTupleType",
64 "TupleType",
65 "DictType",
66 "Field",
67 "TypedDictType",
68 "NamedTupleType",
69 "DataclassType",
70 "StructType",
71 "is_struct",
72 "is_struct_type",
73 "FrozenDictType",
74)
76_PY315_PLUS = sys.version_info >= (3, 15)
79def __dir__():
80 return __all__
83class Type(msgspec.Struct):
84 """The base Type."""
87class Metadata(Type):
88 """A type wrapping a subtype with additional metadata.
90 Parameters
91 ----------
92 type: Type
93 The subtype.
94 extra_json_schema: dict, optional
95 A dict of extra fields to set for the subtype when generating a
96 json-schema.
97 extra: dict, optional
98 A dict of extra user-defined metadata attached to the subtype.
99 """
101 type: Type
102 extra_json_schema: dict | None = None
103 extra: dict | None = None
106class AnyType(Type):
107 """A type corresponding to `typing.Any`."""
110class NoneType(Type):
111 """A type corresponding to `None`."""
114class BoolType(Type):
115 """A type corresponding to `bool`."""
118class IntType(Type):
119 """A type corresponding to `int`.
121 Parameters
122 ----------
123 gt: int, optional
124 If set, an instance of this type must be greater than ``gt``.
125 ge: int, optional
126 If set, an instance of this type must be greater than or equal to ``ge``.
127 lt: int, optional
128 If set, an instance of this type must be less than to ``lt``.
129 le: int, optional
130 If set, an instance of this type must be less than or equal to ``le``.
131 multiple_of: int, optional
132 If set, an instance of this type must be a multiple of ``multiple_of``.
133 """
135 gt: int | None = None
136 ge: int | None = None
137 lt: int | None = None
138 le: int | None = None
139 multiple_of: int | None = None
142class FloatType(Type):
143 """A type corresponding to `float`.
145 Parameters
146 ----------
147 gt: float, optional
148 If set, an instance of this type must be greater than ``gt``.
149 ge: float, optional
150 If set, an instance of this type must be greater than or equal to ``ge``.
151 lt: float, optional
152 If set, an instance of this type must be less than to ``lt``.
153 le: float, optional
154 If set, an instance of this type must be less than or equal to ``le``.
155 multiple_of: float, optional
156 If set, an instance of this type must be a multiple of ``multiple_of``.
157 """
159 gt: float | None = None
160 ge: float | None = None
161 lt: float | None = None
162 le: float | None = None
163 multiple_of: float | None = None
166class StrType(Type):
167 """A type corresponding to `str`.
169 Parameters
170 ----------
171 min_length: int, optional
172 If set, an instance of this type must have length greater than or equal
173 to ``min_length``.
174 max_length: int, optional
175 If set, an instance of this type must have length less than or equal
176 to ``max_length``.
177 pattern: str, optional
178 If set, an instance of this type must match against this regex pattern.
179 Note that the pattern is treated as **unanchored**.
180 """
182 min_length: int | None = None
183 max_length: int | None = None
184 pattern: str | None = None
187class BytesType(Type):
188 """A type corresponding to `bytes`.
190 Parameters
191 ----------
192 min_length: int, optional
193 If set, an instance of this type must have length greater than or equal
194 to ``min_length``.
195 max_length: int, optional
196 If set, an instance of this type must have length less than or equal
197 to ``max_length``.
198 """
200 min_length: int | None = None
201 max_length: int | None = None
204class ByteArrayType(Type):
205 """A type corresponding to `bytearray`.
207 Parameters
208 ----------
209 min_length: int, optional
210 If set, an instance of this type must have length greater than or equal
211 to ``min_length``.
212 max_length: int, optional
213 If set, an instance of this type must have length less than or equal
214 to ``max_length``.
215 """
217 min_length: int | None = None
218 max_length: int | None = None
221class MemoryViewType(Type):
222 """A type corresponding to `memoryview`.
224 Parameters
225 ----------
226 min_length: int, optional
227 If set, an instance of this type must have length greater than or equal
228 to ``min_length``.
229 max_length: int, optional
230 If set, an instance of this type must have length less than or equal
231 to ``max_length``.
232 """
234 min_length: int | None = None
235 max_length: int | None = None
238class DateTimeType(Type):
239 """A type corresponding to `datetime.datetime`.
241 Parameters
242 ----------
243 tz: bool
244 The timezone-requirements for an instance of this type. ``True``
245 indicates a timezone-aware value is required, ``False`` indicates a
246 timezone-naive value is required. The default is ``None``, which
247 accepts either timezone-aware or timezone-naive values.
248 """
250 tz: bool | None = None
253class TimeType(Type):
254 """A type corresponding to `datetime.time`.
256 Parameters
257 ----------
258 tz: bool
259 The timezone-requirements for an instance of this type. ``True``
260 indicates a timezone-aware value is required, ``False`` indicates a
261 timezone-aware value is required. The default is ``None``, which
262 accepts either timezone-aware or timezone-naive values.
263 """
265 tz: bool | None = None
268class DateType(Type):
269 """A type corresponding to `datetime.date`."""
272class TimeDeltaType(Type):
273 """A type corresponding to `datetime.timedelta`."""
276class UUIDType(Type):
277 """A type corresponding to `uuid.UUID`."""
280class DecimalType(Type):
281 """A type corresponding to `decimal.Decimal`."""
284class ExtType(Type):
285 """A type corresponding to `msgspec.msgpack.Ext`."""
288class RawType(Type):
289 """A type corresponding to `msgspec.Raw`."""
292class EnumType(Type):
293 """A type corresponding to an `enum.Enum` type.
295 Parameters
296 ----------
297 cls: type
298 The corresponding `enum.Enum` type.
299 """
301 cls: type[enum.Enum]
304class LiteralType(Type):
305 """A type corresponding to a `typing.Literal` type.
307 Parameters
308 ----------
309 values: tuple
310 A tuple of possible values for this literal instance. Only `bool`,
311 `str`, `int`, or `None` literals are supported, and a single literal
312 may mix these types (e.g. ``Literal[1, None]``).
313 """
315 values: tuple[bool | str | int | None, ...]
318class CustomType(Type):
319 """A custom type.
321 Parameters
322 ----------
323 cls: type
324 The corresponding custom type.
325 """
327 cls: type
330class UnionType(Type):
331 """A union type.
333 Parameters
334 ----------
335 types: tuple[type, ...]
336 A tuple of possible types for this union.
337 """
339 types: tuple[Type, ...]
341 @property
342 def includes_none(self) -> bool:
343 """A helper for checking whether ``None`` is included in this union."""
344 return any(isinstance(t, NoneType) for t in self.types)
347class CollectionType(Type):
348 """A collection type.
350 This is the base type shared by collection types like `ListType`,
351 `SetType`, etc.
353 Parameters
354 ----------
355 item_type: Type
356 The item type.
357 min_length: int, optional
358 If set, an instance of this type must have length greater than or equal
359 to ``min_length``.
360 max_length: int, optional
361 If set, an instance of this type must have length less than or equal
362 to ``max_length``.
363 """
365 item_type: Type
366 min_length: int | None = None
367 max_length: int | None = None
370class ListType(CollectionType):
371 """A type corresponding to a `list`.
373 Parameters
374 ----------
375 item_type: Type
376 The item type.
377 min_length: int, optional
378 If set, an instance of this type must have length greater than or equal
379 to ``min_length``.
380 max_length: int, optional
381 If set, an instance of this type must have length less than or equal
382 to ``max_length``.
383 """
386class VarTupleType(CollectionType):
387 """A type corresponding to a variadic `tuple`.
389 Parameters
390 ----------
391 item_type: Type
392 The item type.
393 min_length: int, optional
394 If set, an instance of this type must have length greater than or equal
395 to ``min_length``.
396 max_length: int, optional
397 If set, an instance of this type must have length less than or equal
398 to ``max_length``.
399 """
402class SetType(CollectionType):
403 """A type corresponding to a `set`.
405 Parameters
406 ----------
407 item_type: Type
408 The item type.
409 min_length: int, optional
410 If set, an instance of this type must have length greater than or equal
411 to ``min_length``.
412 max_length: int, optional
413 If set, an instance of this type must have length less than or equal
414 to ``max_length``.
415 """
418class FrozenSetType(CollectionType):
419 """A type corresponding to a `frozenset`.
421 Parameters
422 ----------
423 item_type: Type
424 The item type.
425 min_length: int, optional
426 If set, an instance of this type must have length greater than or equal
427 to ``min_length``.
428 max_length: int, optional
429 If set, an instance of this type must have length less than or equal
430 to ``max_length``.
431 """
434class TupleType(Type):
435 """A type corresponding to `tuple`.
437 Parameters
438 ----------
439 item_types: tuple[Type, ...]
440 A tuple of types for each element in the tuple.
441 """
443 item_types: tuple[Type, ...]
446class DictType(Type):
447 """A type corresponding to `dict`.
449 Parameters
450 ----------
451 key_type: Type
452 The key type.
453 value_type: Type
454 The value type.
455 min_length: int, optional
456 If set, an instance of this type must have length greater than or equal
457 to ``min_length``.
458 max_length: int, optional
459 If set, an instance of this type must have length less than or equal
460 to ``max_length``.
461 """
463 key_type: Type
464 value_type: Type
465 min_length: int | None = None
466 max_length: int | None = None
469class FrozenDictType(Type):
470 """A type corresponding to `frozendict`.
472 Can only be emitted on Python 3.15+.
474 Parameters
475 ----------
476 key_type: Type
477 The key type.
478 value_type: Type
479 The value type.
480 min_length: int, optional
481 If set, an instance of this type must have length greater than or equal
482 to ``min_length``.
483 max_length: int, optional
484 If set, an instance of this type must have length less than or equal
485 to ``max_length``.
486 """
488 key_type: Type
489 value_type: Type
490 min_length: Union[int, None] = None
491 max_length: Union[int, None] = None
494class Field(msgspec.Struct):
495 """A record describing a field in an object-like type.
497 Parameters
498 ----------
499 name: str
500 The field name as seen by Python code (e.g. ``field_one``).
501 encode_name: str
502 The name used when encoding/decoding the field. This may differ if
503 the field is renamed (e.g. ``fieldOne``).
504 type: Type
505 The field type.
506 required: bool, optional
507 Whether the field is required. Note that if `required` is False doesn't
508 necessarily mean that `default` or `default_factory` will be set -
509 optional fields may exist with no default value.
510 default: Any, optional
511 A default value for the field. Will be `NODEFAULT` if no default value
512 is set.
513 default_factory: Any, optional
514 A callable that creates a default value for the field. Will be
515 `NODEFAULT` if no ``default_factory`` is set.
516 """
518 name: str
519 encode_name: str
520 type: Type
521 required: bool = True
522 default: Any = msgspec.field(default_factory=lambda: NODEFAULT)
523 default_factory: Any = msgspec.field(default_factory=lambda: NODEFAULT)
526class TypedDictType(Type):
527 """A type corresponding to a `typing.TypedDict` type.
529 Parameters
530 ----------
531 cls: type
532 The corresponding TypedDict type.
533 fields: tuple[Field, ...]
534 A tuple of fields in the TypedDict.
535 """
537 cls: type
538 fields: tuple[Field, ...]
541class NamedTupleType(Type):
542 """A type corresponding to a `typing.NamedTuple` type.
544 Parameters
545 ----------
546 cls: type
547 The corresponding NamedTuple type.
548 fields: tuple[Field, ...]
549 A tuple of fields in the NamedTuple.
550 """
552 cls: type
553 fields: tuple[Field, ...]
556class DataclassType(Type):
557 """A type corresponding to a `dataclasses` or `attrs` type.
559 Parameters
560 ----------
561 cls: type
562 The corresponding dataclass type.
563 fields: tuple[Field, ...]
564 A tuple of fields in the dataclass.
565 """
567 cls: type
568 fields: tuple[Field, ...]
571class StructType(Type):
572 """A type corresponding to a `msgspec.Struct` type.
574 Parameters
575 ----------
576 cls: type
577 The corresponding Struct type.
578 fields: tuple[Field, ...]
579 A tuple of fields in the Struct.
580 tag_field: str or None, optional
581 If set, the field name used for the tag in a tagged union.
582 tag: str, int, or None, optional
583 If set, the value used for the tag in a tagged union.
584 array_like: bool, optional
585 Whether the struct is encoded as an array rather than an object.
586 forbid_unknown_fields: bool, optional
587 If ``False`` (the default) unknown fields are ignored when decoding. If
588 ``True`` any unknown fields will result in an error.
589 """
591 cls: type[msgspec.Struct]
592 fields: tuple[Field, ...]
593 tag_field: str | None = None
594 tag: str | int | None = None
595 array_like: bool = False
596 forbid_unknown_fields: bool = False
599def multi_type_info(types: Iterable[Any]) -> tuple[Type, ...]:
600 """Get information about multiple msgspec-compatible types.
602 Parameters
603 ----------
604 types: an iterable of types
605 The types to get info about.
607 Returns
608 -------
609 tuple[Type, ...]
611 Examples
612 --------
613 >>> msgspec.inspect.multi_type_info([int, float, list[str]]) # doctest: +NORMALIZE_WHITESPACE
614 (IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
615 FloatType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
616 ListType(item_type=StrType(min_length=None, max_length=None, pattern=None),
617 min_length=None, max_length=None))
618 """
619 return _Translator(types).run()
622def type_info(type: Any) -> Type:
623 """Get information about a msgspec-compatible type.
625 Note that if you need to inspect multiple types it's more efficient to call
626 `multi_type_info` once with a sequence of types than calling `type_info`
627 multiple times.
629 Parameters
630 ----------
631 type: type
632 The type to get info about.
634 Returns
635 -------
636 Type
638 Examples
639 --------
640 >>> msgspec.inspect.type_info(bool)
641 BoolType()
643 >>> msgspec.inspect.type_info(int)
644 IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None)
646 >>> msgspec.inspect.type_info(list[int]) # doctest: +NORMALIZE_WHITESPACE
647 ListType(item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
648 min_length=None, max_length=None)
649 """
650 return multi_type_info([type])[0]
653# Implementation details
654def _origin_args_metadata(t):
655 # Strip wrappers (Annotated, NewType, Final) until we hit a concrete type
656 metadata = []
657 while True:
658 try:
659 origin = _CONCRETE_TYPES.get(t)
660 except TypeError:
661 # t is not hashable
662 origin = None
664 if origin is not None:
665 args = None
666 break
668 origin = getattr(t, "__origin__", None)
669 if origin is not None:
670 if type(t) is _AnnotatedAlias:
671 metadata.extend(m for m in t.__metadata__ if type(m) is msgspec.Meta)
672 t = origin
673 elif origin == Final:
674 t = t.__args__[0]
675 elif type(origin) is _TypeAliasType:
676 t = origin.__value__[t.__args__]
677 else:
678 args = getattr(t, "__args__", None)
679 origin = _CONCRETE_TYPES.get(origin, origin)
680 break
681 else:
682 supertype = getattr(t, "__supertype__", None)
683 if supertype is not None:
684 t = supertype
685 elif type(t) is _TypeAliasType:
686 t = t.__value__
687 else:
688 origin = t
689 args = None
690 break
692 if type(origin) is _types_UnionType:
693 args = origin.__args__
694 origin = Union
695 return origin, args, tuple(metadata)
698def _sort_literal_args(args):
699 # `Literal` may mix value types (e.g. `Literal[1, None]`), which a plain
700 # `sorted` can't order since Python 3 forbids comparing across types. Sort
701 # by type name first so members of the same type stay ordered as before,
702 # while mixed-type literals are grouped deterministically instead of
703 # crashing.
704 try:
705 return tuple(sorted(args, key=lambda x: (type(x).__name__, x)))
706 except TypeError:
707 return tuple(args)
710def _is_enum(t):
711 return type(t) is enum.EnumMeta
714def _is_dataclass(t):
715 return hasattr(t, "__dataclass_fields__")
718def _is_attrs(t):
719 return hasattr(t, "__attrs_attrs__")
722def _is_typeddict(t):
723 try:
724 return issubclass(t, dict) and hasattr(t, "__total__")
725 except TypeError:
726 return False
729def _is_namedtuple(t):
730 try:
731 return issubclass(t, tuple) and hasattr(t, "_fields")
732 except TypeError:
733 return False
736def _merge_json(a, b):
737 if b:
738 a = a.copy()
739 for key, b_val in b.items():
740 if key in a:
741 a_val = a[key]
742 if isinstance(a_val, dict) and isinstance(b_val, dict):
743 a[key] = _merge_json(a_val, b_val)
744 elif isinstance(a_val, (list, tuple)) and isinstance(
745 b_val, (list, tuple)
746 ):
747 a[key] = list(a_val) + list(b_val)
748 else:
749 a[key] = b_val
750 else:
751 a[key] = b_val
752 return a
755class _Translator:
756 def __init__(self, types):
757 self.types = tuple(types)
758 self.type_hints = {}
759 self.cache = {}
761 def _get_class_annotations(self, t):
762 """A cached version of `get_class_annotations`"""
763 try:
764 return self.type_hints[t]
765 except KeyError:
766 out = self.type_hints[t] = _get_class_annotations(t)
767 return out
769 def run(self):
770 # First construct a decoder to validate the types are valid
771 from ._core import MsgpackDecoder
773 MsgpackDecoder(tuple[self.types])
774 return tuple(self.translate(t) for t in self.types)
776 def translate(self, typ):
777 t, args, metadata = _origin_args_metadata(typ)
779 # Extract and merge components of any `Meta` annotations
780 constrs = {}
781 extra_json_schema = {}
782 extra = {}
783 for meta in metadata:
784 for attr in (
785 "ge",
786 "gt",
787 "le",
788 "lt",
789 "multiple_of",
790 "pattern",
791 "min_length",
792 "max_length",
793 "tz",
794 ):
795 if (val := getattr(meta, attr)) is not None:
796 constrs[attr] = val
797 for attr in ("title", "description", "examples"):
798 if (val := getattr(meta, attr)) is not None:
799 extra_json_schema[attr] = val
800 if meta.extra_json_schema is not None:
801 extra_json_schema = _merge_json(
802 extra_json_schema,
803 _to_builtins(meta.extra_json_schema, str_keys=True),
804 )
805 if meta.extra is not None:
806 extra.update(meta.extra)
808 out = self._translate_inner(t, args, **constrs)
809 if extra_json_schema or extra:
810 # If extra metadata is present, wrap the output type in a Metadata
811 # wrapper object
812 return Metadata(
813 out, extra_json_schema=extra_json_schema or None, extra=extra or None
814 )
815 return out
817 def _translate_inner(
818 self,
819 t,
820 args,
821 ge=None,
822 gt=None,
823 le=None,
824 lt=None,
825 multiple_of=None,
826 pattern=None,
827 min_length=None,
828 max_length=None,
829 tz=None,
830 ):
831 if t is Any:
832 return AnyType()
833 elif isinstance(t, TypeVar):
834 if t.__bound__ is not None:
835 return self.translate(t.__bound__)
836 return AnyType()
837 elif t is None or t is type(None):
838 return NoneType()
839 elif t is bool:
840 return BoolType()
841 elif t is int:
842 return IntType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
843 elif t is float:
844 return FloatType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
845 elif t is str:
846 return StrType(
847 min_length=min_length, max_length=max_length, pattern=pattern
848 )
849 elif t is bytes:
850 return BytesType(min_length=min_length, max_length=max_length)
851 elif t is bytearray:
852 return ByteArrayType(min_length=min_length, max_length=max_length)
853 elif t is memoryview:
854 return MemoryViewType(min_length=min_length, max_length=max_length)
855 elif t is datetime.datetime:
856 return DateTimeType(tz=tz)
857 elif t is datetime.time:
858 return TimeType(tz=tz)
859 elif t is datetime.date:
860 return DateType()
861 elif t is datetime.timedelta:
862 return TimeDeltaType()
863 elif t is uuid.UUID:
864 return UUIDType()
865 elif t is decimal.Decimal:
866 return DecimalType()
867 elif t is msgspec.Raw:
868 return RawType()
869 elif t is msgspec.msgpack.Ext:
870 return ExtType()
871 elif t is list:
872 return ListType(
873 self.translate(args[0]) if args else AnyType(),
874 min_length=min_length,
875 max_length=max_length,
876 )
877 elif t is set:
878 return SetType(
879 self.translate(args[0]) if args else AnyType(),
880 min_length=min_length,
881 max_length=max_length,
882 )
883 elif t is frozenset:
884 return FrozenSetType(
885 self.translate(args[0]) if args else AnyType(),
886 min_length=min_length,
887 max_length=max_length,
888 )
889 elif t is tuple:
890 # Handle an annoying compatibility issue:
891 # - Tuple[()] has args == ((),)
892 # - tuple[()] has args == ()
893 if args == ((),):
894 args = ()
895 if args is None:
896 return VarTupleType(
897 AnyType(), min_length=min_length, max_length=max_length
898 )
899 elif len(args) == 2 and args[-1] is ...:
900 return VarTupleType(
901 self.translate(args[0]),
902 min_length=min_length,
903 max_length=max_length,
904 )
905 else:
906 return TupleType(tuple(self.translate(a) for a in args))
907 elif t is dict:
908 return DictType(
909 self.translate(args[0]) if args else AnyType(),
910 self.translate(args[1]) if args else AnyType(),
911 min_length=min_length,
912 max_length=max_length,
913 )
914 elif _PY315_PLUS and t is frozendict: # noqa: F821
915 return FrozenDictType(
916 self.translate(args[0]) if args else AnyType(),
917 self.translate(args[1]) if args else AnyType(),
918 min_length=min_length,
919 max_length=max_length,
920 )
921 elif t is Union:
922 args = tuple(self.translate(a) for a in args if a is not _UnsetType)
923 return args[0] if len(args) == 1 else UnionType(args)
924 elif t is Literal:
925 return LiteralType(_sort_literal_args(args))
926 elif _is_enum(t):
927 return EnumType(t)
928 elif is_struct_type(t):
929 cls = t[args] if args else t
930 if cls in self.cache:
931 return self.cache[cls]
932 config = t.__struct_config__
933 self.cache[cls] = out = StructType(
934 cls,
935 (),
936 tag_field=config.tag_field,
937 tag=config.tag,
938 array_like=config.array_like,
939 forbid_unknown_fields=config.forbid_unknown_fields,
940 )
942 hints = self._get_class_annotations(cls)
943 npos = len(t.__struct_fields__) - len(t.__struct_defaults__)
944 fields = []
945 for name, encode_name, default_obj in zip(
946 t.__struct_fields__,
947 t.__struct_encode_fields__,
948 (NODEFAULT,) * npos + t.__struct_defaults__,
949 ):
950 if default_obj is NODEFAULT:
951 required = True
952 default = default_factory = NODEFAULT
953 elif isinstance(default_obj, _Factory):
954 required = False
955 default = NODEFAULT
956 default_factory = default_obj.factory
957 else:
958 required = False
959 default = NODEFAULT if default_obj is UNSET else default_obj
960 default_factory = NODEFAULT
962 field = Field(
963 name=name,
964 encode_name=encode_name,
965 type=self.translate(hints[name]),
966 required=required,
967 default=default,
968 default_factory=default_factory,
969 )
970 fields.append(field)
972 out.fields = tuple(fields)
973 return out
974 elif _is_typeddict(t):
975 cls = t[args] if args else t
976 if cls in self.cache:
977 return self.cache[cls]
978 self.cache[cls] = out = TypedDictType(cls, ())
979 hints, required = _get_typeddict_info(cls)
980 out.fields = tuple(
981 Field(
982 name=name,
983 encode_name=name,
984 type=self.translate(field_type),
985 required=name in required,
986 )
987 for name, field_type in sorted(hints.items())
988 )
989 return out
990 elif _is_dataclass(t) or _is_attrs(t):
991 cls = t[args] if args else t
992 if cls in self.cache:
993 return self.cache[cls]
994 self.cache[cls] = out = DataclassType(cls, ())
995 _, info, defaults, _, _ = _get_dataclass_info(cls)
996 defaults = ((NODEFAULT,) * (len(info) - len(defaults))) + defaults
997 fields = []
998 for (name, typ, is_factory), default_obj in zip(info, defaults):
999 if default_obj is NODEFAULT:
1000 required = True
1001 default = default_factory = NODEFAULT
1002 elif is_factory:
1003 required = False
1004 default = NODEFAULT
1005 default_factory = default_obj
1006 else:
1007 required = False
1008 default = NODEFAULT if default_obj is UNSET else default_obj
1009 default_factory = NODEFAULT
1011 fields.append(
1012 Field(
1013 name=name,
1014 encode_name=name,
1015 type=self.translate(typ),
1016 required=required,
1017 default=default,
1018 default_factory=default_factory,
1019 )
1020 )
1021 out.fields = tuple(fields)
1022 return out
1023 elif _is_namedtuple(t):
1024 cls = t[args] if args else t
1025 if cls in self.cache:
1026 return self.cache[cls]
1027 self.cache[cls] = out = NamedTupleType(cls, ())
1028 hints = self._get_class_annotations(cls)
1029 out.fields = tuple(
1030 Field(
1031 name=name,
1032 encode_name=name,
1033 type=self.translate(hints.get(name, Any)),
1034 required=name not in t._field_defaults,
1035 default=t._field_defaults.get(name, NODEFAULT),
1036 )
1037 for name in t._fields
1038 )
1039 return out
1040 else:
1041 return CustomType(t)