Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/msgspec/inspect.py: 41%
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 uuid
7from collections.abc import Iterable
8from typing import (
9 Any,
10 Final,
11 Literal,
12 Tuple,
13 Type as typing_Type,
14 TypeVar,
15 Union,
16)
18try:
19 from types import UnionType as _types_UnionType # type: ignore
20except Exception:
21 _types_UnionType = type("UnionType", (), {}) # type: ignore
23try:
24 from typing import TypeAliasType as _TypeAliasType # type: ignore
25except Exception:
26 _TypeAliasType = type("TypeAliasType", (), {}) # type: ignore
28import msgspec
29from msgspec import NODEFAULT, UNSET, UnsetType as _UnsetType
31from ._core import ( # type: ignore
32 Factory as _Factory,
33 to_builtins as _to_builtins,
34)
35from ._typing_utils import is_struct, is_struct_type
36from ._utils import ( # type: ignore
37 _CONCRETE_TYPES,
38 _AnnotatedAlias,
39 get_class_annotations as _get_class_annotations,
40 get_dataclass_info as _get_dataclass_info,
41 get_typeddict_info as _get_typeddict_info,
42)
44__all__ = (
45 "type_info",
46 "multi_type_info",
47 "Type",
48 "Metadata",
49 "AnyType",
50 "NoneType",
51 "BoolType",
52 "IntType",
53 "FloatType",
54 "StrType",
55 "BytesType",
56 "ByteArrayType",
57 "MemoryViewType",
58 "DateTimeType",
59 "TimeType",
60 "DateType",
61 "TimeDeltaType",
62 "UUIDType",
63 "DecimalType",
64 "ExtType",
65 "RawType",
66 "EnumType",
67 "LiteralType",
68 "CustomType",
69 "UnionType",
70 "CollectionType",
71 "ListType",
72 "SetType",
73 "FrozenSetType",
74 "VarTupleType",
75 "TupleType",
76 "DictType",
77 "Field",
78 "TypedDictType",
79 "NamedTupleType",
80 "DataclassType",
81 "StructType",
82 "is_struct",
83 "is_struct_type",
84)
87def __dir__():
88 return __all__
91class Type(msgspec.Struct):
92 """The base Type."""
95class Metadata(Type):
96 """A type wrapping a subtype with additional metadata.
98 Parameters
99 ----------
100 type: Type
101 The subtype.
102 extra_json_schema: dict, optional
103 A dict of extra fields to set for the subtype when generating a
104 json-schema.
105 extra: dict, optional
106 A dict of extra user-defined metadata attached to the subtype.
107 """
109 type: Type
110 extra_json_schema: Union[dict, None] = None
111 extra: Union[dict, None] = None
114class AnyType(Type):
115 """A type corresponding to `typing.Any`."""
118class NoneType(Type):
119 """A type corresponding to `None`."""
122class BoolType(Type):
123 """A type corresponding to `bool`."""
126class IntType(Type):
127 """A type corresponding to `int`.
129 Parameters
130 ----------
131 gt: int, optional
132 If set, an instance of this type must be greater than ``gt``.
133 ge: int, optional
134 If set, an instance of this type must be greater than or equal to ``ge``.
135 lt: int, optional
136 If set, an instance of this type must be less than to ``lt``.
137 le: int, optional
138 If set, an instance of this type must be less than or equal to ``le``.
139 multiple_of: int, optional
140 If set, an instance of this type must be a multiple of ``multiple_of``.
141 """
143 gt: Union[int, None] = None
144 ge: Union[int, None] = None
145 lt: Union[int, None] = None
146 le: Union[int, None] = None
147 multiple_of: Union[int, None] = None
150class FloatType(Type):
151 """A type corresponding to `float`.
153 Parameters
154 ----------
155 gt: float, optional
156 If set, an instance of this type must be greater than ``gt``.
157 ge: float, optional
158 If set, an instance of this type must be greater than or equal to ``ge``.
159 lt: float, optional
160 If set, an instance of this type must be less than to ``lt``.
161 le: float, optional
162 If set, an instance of this type must be less than or equal to ``le``.
163 multiple_of: float, optional
164 If set, an instance of this type must be a multiple of ``multiple_of``.
165 """
167 gt: Union[float, None] = None
168 ge: Union[float, None] = None
169 lt: Union[float, None] = None
170 le: Union[float, None] = None
171 multiple_of: Union[float, None] = None
174class StrType(Type):
175 """A type corresponding to `str`.
177 Parameters
178 ----------
179 min_length: int, optional
180 If set, an instance of this type must have length greater than or equal
181 to ``min_length``.
182 max_length: int, optional
183 If set, an instance of this type must have length less than or equal
184 to ``max_length``.
185 pattern: str, optional
186 If set, an instance of this type must match against this regex pattern.
187 Note that the pattern is treated as **unanchored**.
188 """
190 min_length: Union[int, None] = None
191 max_length: Union[int, None] = None
192 pattern: Union[str, None] = None
195class BytesType(Type):
196 """A type corresponding to `bytes`.
198 Parameters
199 ----------
200 min_length: int, optional
201 If set, an instance of this type must have length greater than or equal
202 to ``min_length``.
203 max_length: int, optional
204 If set, an instance of this type must have length less than or equal
205 to ``max_length``.
206 """
208 min_length: Union[int, None] = None
209 max_length: Union[int, None] = None
212class ByteArrayType(Type):
213 """A type corresponding to `bytearray`.
215 Parameters
216 ----------
217 min_length: int, optional
218 If set, an instance of this type must have length greater than or equal
219 to ``min_length``.
220 max_length: int, optional
221 If set, an instance of this type must have length less than or equal
222 to ``max_length``.
223 """
225 min_length: Union[int, None] = None
226 max_length: Union[int, None] = None
229class MemoryViewType(Type):
230 """A type corresponding to `memoryview`.
232 Parameters
233 ----------
234 min_length: int, optional
235 If set, an instance of this type must have length greater than or equal
236 to ``min_length``.
237 max_length: int, optional
238 If set, an instance of this type must have length less than or equal
239 to ``max_length``.
240 """
242 min_length: Union[int, None] = None
243 max_length: Union[int, None] = None
246class DateTimeType(Type):
247 """A type corresponding to `datetime.datetime`.
249 Parameters
250 ----------
251 tz: bool
252 The timezone-requirements for an instance of this type. ``True``
253 indicates a timezone-aware value is required, ``False`` indicates a
254 timezone-aware value is required. The default is ``None``, which
255 accepts either timezone-aware or timezone-naive values.
256 """
258 tz: Union[bool, None] = None
261class TimeType(Type):
262 """A type corresponding to `datetime.time`.
264 Parameters
265 ----------
266 tz: bool
267 The timezone-requirements for an instance of this type. ``True``
268 indicates a timezone-aware value is required, ``False`` indicates a
269 timezone-aware value is required. The default is ``None``, which
270 accepts either timezone-aware or timezone-naive values.
271 """
273 tz: Union[bool, None] = None
276class DateType(Type):
277 """A type corresponding to `datetime.date`."""
280class TimeDeltaType(Type):
281 """A type corresponding to `datetime.timedelta`."""
284class UUIDType(Type):
285 """A type corresponding to `uuid.UUID`."""
288class DecimalType(Type):
289 """A type corresponding to `decimal.Decimal`."""
292class ExtType(Type):
293 """A type corresponding to `msgspec.msgpack.Ext`."""
296class RawType(Type):
297 """A type corresponding to `msgspec.Raw`."""
300class EnumType(Type):
301 """A type corresponding to an `enum.Enum` type.
303 Parameters
304 ----------
305 cls: type
306 The corresponding `enum.Enum` type.
307 """
309 cls: typing_Type[enum.Enum]
312class LiteralType(Type):
313 """A type corresponding to a `typing.Literal` type.
315 Parameters
316 ----------
317 values: tuple
318 A tuple of possible values for this literal instance. Only `str` or
319 `int` literals are supported.
320 """
322 values: Union[Tuple[str, ...], Tuple[int, ...]]
325class CustomType(Type):
326 """A custom type.
328 Parameters
329 ----------
330 cls: type
331 The corresponding custom type.
332 """
334 cls: type
337class UnionType(Type):
338 """A union type.
340 Parameters
341 ----------
342 types: Tuple[Type, ...]
343 A tuple of possible types for this union.
344 """
346 types: Tuple[Type, ...]
348 @property
349 def includes_none(self) -> bool:
350 """A helper for checking whether ``None`` is included in this union."""
351 return any(isinstance(t, NoneType) for t in self.types)
354class CollectionType(Type):
355 """A collection type.
357 This is the base type shared by collection types like `ListType`,
358 `SetType`, etc.
360 Parameters
361 ----------
362 item_type: Type
363 The item type.
364 min_length: int, optional
365 If set, an instance of this type must have length greater than or equal
366 to ``min_length``.
367 max_length: int, optional
368 If set, an instance of this type must have length less than or equal
369 to ``max_length``.
370 """
372 item_type: Type
373 min_length: Union[int, None] = None
374 max_length: Union[int, None] = None
377class ListType(CollectionType):
378 """A type corresponding to a `list`.
380 Parameters
381 ----------
382 item_type: Type
383 The item type.
384 min_length: int, optional
385 If set, an instance of this type must have length greater than or equal
386 to ``min_length``.
387 max_length: int, optional
388 If set, an instance of this type must have length less than or equal
389 to ``max_length``.
390 """
393class VarTupleType(CollectionType):
394 """A type corresponding to a variadic `tuple`.
396 Parameters
397 ----------
398 item_type: Type
399 The item type.
400 min_length: int, optional
401 If set, an instance of this type must have length greater than or equal
402 to ``min_length``.
403 max_length: int, optional
404 If set, an instance of this type must have length less than or equal
405 to ``max_length``.
406 """
409class SetType(CollectionType):
410 """A type corresponding to a `set`.
412 Parameters
413 ----------
414 item_type: Type
415 The item type.
416 min_length: int, optional
417 If set, an instance of this type must have length greater than or equal
418 to ``min_length``.
419 max_length: int, optional
420 If set, an instance of this type must have length less than or equal
421 to ``max_length``.
422 """
425class FrozenSetType(CollectionType):
426 """A type corresponding to a `frozenset`.
428 Parameters
429 ----------
430 item_type: Type
431 The item type.
432 min_length: int, optional
433 If set, an instance of this type must have length greater than or equal
434 to ``min_length``.
435 max_length: int, optional
436 If set, an instance of this type must have length less than or equal
437 to ``max_length``.
438 """
441class TupleType(Type):
442 """A type corresponding to `tuple`.
444 Parameters
445 ----------
446 item_types: Tuple[Type, ...]
447 A tuple of types for each element in the tuple.
448 """
450 item_types: Tuple[Type, ...]
453class DictType(Type):
454 """A type corresponding to `dict`.
456 Parameters
457 ----------
458 key_type: Type
459 The key type.
460 value_type: Type
461 The value type.
462 min_length: int, optional
463 If set, an instance of this type must have length greater than or equal
464 to ``min_length``.
465 max_length: int, optional
466 If set, an instance of this type must have length less than or equal
467 to ``max_length``.
468 """
470 key_type: Type
471 value_type: Type
472 min_length: Union[int, None] = None
473 max_length: Union[int, None] = None
476class Field(msgspec.Struct):
477 """A record describing a field in an object-like type.
479 Parameters
480 ----------
481 name: str
482 The field name as seen by Python code (e.g. ``field_one``).
483 encode_name: str
484 The name used when encoding/decoding the field. This may differ if
485 the field is renamed (e.g. ``fieldOne``).
486 type: Type
487 The field type.
488 required: bool, optional
489 Whether the field is required. Note that if `required` is False doesn't
490 necessarily mean that `default` or `default_factory` will be set -
491 optional fields may exist with no default value.
492 default: Any, optional
493 A default value for the field. Will be `NODEFAULT` if no default value
494 is set.
495 default_factory: Any, optional
496 A callable that creates a default value for the field. Will be
497 `NODEFAULT` if no ``default_factory`` is set.
498 """
500 name: str
501 encode_name: str
502 type: Type
503 required: bool = True
504 default: Any = msgspec.field(default_factory=lambda: NODEFAULT)
505 default_factory: Any = msgspec.field(default_factory=lambda: NODEFAULT)
508class TypedDictType(Type):
509 """A type corresponding to a `typing.TypedDict` type.
511 Parameters
512 ----------
513 cls: type
514 The corresponding TypedDict type.
515 fields: Tuple[Field, ...]
516 A tuple of fields in the TypedDict.
517 """
519 cls: type
520 fields: Tuple[Field, ...]
523class NamedTupleType(Type):
524 """A type corresponding to a `typing.NamedTuple` type.
526 Parameters
527 ----------
528 cls: type
529 The corresponding NamedTuple type.
530 fields: Tuple[Field, ...]
531 A tuple of fields in the NamedTuple.
532 """
534 cls: type
535 fields: Tuple[Field, ...]
538class DataclassType(Type):
539 """A type corresponding to a `dataclasses` or `attrs` type.
541 Parameters
542 ----------
543 cls: type
544 The corresponding dataclass type.
545 fields: Tuple[Field, ...]
546 A tuple of fields in the dataclass.
547 """
549 cls: type
550 fields: Tuple[Field, ...]
553class StructType(Type):
554 """A type corresponding to a `msgspec.Struct` type.
556 Parameters
557 ----------
558 cls: type
559 The corresponding Struct type.
560 fields: Tuple[Field, ...]
561 A tuple of fields in the Struct.
562 tag_field: str or None, optional
563 If set, the field name used for the tag in a tagged union.
564 tag: str, int, or None, optional
565 If set, the value used for the tag in a tagged union.
566 array_like: bool, optional
567 Whether the struct is encoded as an array rather than an object.
568 forbid_unknown_fields: bool, optional
569 If ``False`` (the default) unknown fields are ignored when decoding. If
570 ``True`` any unknown fields will result in an error.
571 """
573 cls: typing_Type[msgspec.Struct]
574 fields: Tuple[Field, ...]
575 tag_field: Union[str, None] = None
576 tag: Union[str, int, None] = None
577 array_like: bool = False
578 forbid_unknown_fields: bool = False
581def multi_type_info(types: Iterable[Any]) -> tuple[Type, ...]:
582 """Get information about multiple msgspec-compatible types.
584 Parameters
585 ----------
586 types: an iterable of types
587 The types to get info about.
589 Returns
590 -------
591 tuple[Type, ...]
593 Examples
594 --------
595 >>> msgspec.inspect.multi_type_info([int, float, list[str]]) # doctest: +NORMALIZE_WHITESPACE
596 (IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
597 FloatType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
598 ListType(item_type=StrType(min_length=None, max_length=None, pattern=None),
599 min_length=None, max_length=None))
600 """
601 return _Translator(types).run()
604def type_info(type: Any) -> Type:
605 """Get information about a msgspec-compatible type.
607 Note that if you need to inspect multiple types it's more efficient to call
608 `multi_type_info` once with a sequence of types than calling `type_info`
609 multiple times.
611 Parameters
612 ----------
613 type: type
614 The type to get info about.
616 Returns
617 -------
618 Type
620 Examples
621 --------
622 >>> msgspec.inspect.type_info(bool)
623 BoolType()
625 >>> msgspec.inspect.type_info(int)
626 IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None)
628 >>> msgspec.inspect.type_info(list[int]) # doctest: +NORMALIZE_WHITESPACE
629 ListType(item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
630 min_length=None, max_length=None)
631 """
632 return multi_type_info([type])[0]
635# Implementation details
636def _origin_args_metadata(t):
637 # Strip wrappers (Annotated, NewType, Final) until we hit a concrete type
638 metadata = []
639 while True:
640 try:
641 origin = _CONCRETE_TYPES.get(t)
642 except TypeError:
643 # t is not hashable
644 origin = None
646 if origin is not None:
647 args = None
648 break
650 origin = getattr(t, "__origin__", None)
651 if origin is not None:
652 if type(t) is _AnnotatedAlias:
653 metadata.extend(m for m in t.__metadata__ if type(m) is msgspec.Meta)
654 t = origin
655 elif origin == Final:
656 t = t.__args__[0]
657 elif type(origin) is _TypeAliasType:
658 t = origin.__value__[t.__args__]
659 else:
660 args = getattr(t, "__args__", None)
661 origin = _CONCRETE_TYPES.get(origin, origin)
662 break
663 else:
664 supertype = getattr(t, "__supertype__", None)
665 if supertype is not None:
666 t = supertype
667 elif type(t) is _TypeAliasType:
668 t = t.__value__
669 else:
670 origin = t
671 args = None
672 break
674 if type(origin) is _types_UnionType:
675 args = origin.__args__
676 origin = Union
677 return origin, args, tuple(metadata)
680def _is_enum(t):
681 return type(t) is enum.EnumMeta
684def _is_dataclass(t):
685 return hasattr(t, "__dataclass_fields__")
688def _is_attrs(t):
689 return hasattr(t, "__attrs_attrs__")
692def _is_typeddict(t):
693 try:
694 return issubclass(t, dict) and hasattr(t, "__total__")
695 except TypeError:
696 return False
699def _is_namedtuple(t):
700 try:
701 return issubclass(t, tuple) and hasattr(t, "_fields")
702 except TypeError:
703 return False
706def _merge_json(a, b):
707 if b:
708 a = a.copy()
709 for key, b_val in b.items():
710 if key in a:
711 a_val = a[key]
712 if isinstance(a_val, dict) and isinstance(b_val, dict):
713 a[key] = _merge_json(a_val, b_val)
714 elif isinstance(a_val, (list, tuple)) and isinstance(
715 b_val, (list, tuple)
716 ):
717 a[key] = list(a_val) + list(b_val)
718 else:
719 a[key] = b_val
720 else:
721 a[key] = b_val
722 return a
725class _Translator:
726 def __init__(self, types):
727 self.types = tuple(types)
728 self.type_hints = {}
729 self.cache = {}
731 def _get_class_annotations(self, t):
732 """A cached version of `get_class_annotations`"""
733 try:
734 return self.type_hints[t]
735 except KeyError:
736 out = self.type_hints[t] = _get_class_annotations(t)
737 return out
739 def run(self):
740 # First construct a decoder to validate the types are valid
741 from ._core import MsgpackDecoder
743 MsgpackDecoder(Tuple[self.types])
744 return tuple(self.translate(t) for t in self.types)
746 def translate(self, typ):
747 t, args, metadata = _origin_args_metadata(typ)
749 # Extract and merge components of any `Meta` annotations
750 constrs = {}
751 extra_json_schema = {}
752 extra = {}
753 for meta in metadata:
754 for attr in (
755 "ge",
756 "gt",
757 "le",
758 "lt",
759 "multiple_of",
760 "pattern",
761 "min_length",
762 "max_length",
763 "tz",
764 ):
765 if (val := getattr(meta, attr)) is not None:
766 constrs[attr] = val
767 for attr in ("title", "description", "examples"):
768 if (val := getattr(meta, attr)) is not None:
769 extra_json_schema[attr] = val
770 if meta.extra_json_schema is not None:
771 extra_json_schema = _merge_json(
772 extra_json_schema,
773 _to_builtins(meta.extra_json_schema, str_keys=True),
774 )
775 if meta.extra is not None:
776 extra.update(meta.extra)
778 out = self._translate_inner(t, args, **constrs)
779 if extra_json_schema or extra:
780 # If extra metadata is present, wrap the output type in a Metadata
781 # wrapper object
782 return Metadata(
783 out, extra_json_schema=extra_json_schema or None, extra=extra or None
784 )
785 return out
787 def _translate_inner(
788 self,
789 t,
790 args,
791 ge=None,
792 gt=None,
793 le=None,
794 lt=None,
795 multiple_of=None,
796 pattern=None,
797 min_length=None,
798 max_length=None,
799 tz=None,
800 ):
801 if t is Any:
802 return AnyType()
803 elif isinstance(t, TypeVar):
804 if t.__bound__ is not None:
805 return self.translate(t.__bound__)
806 return AnyType()
807 elif t is None or t is type(None):
808 return NoneType()
809 elif t is bool:
810 return BoolType()
811 elif t is int:
812 return IntType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
813 elif t is float:
814 return FloatType(ge=ge, gt=gt, le=le, lt=lt, multiple_of=multiple_of)
815 elif t is str:
816 return StrType(
817 min_length=min_length, max_length=max_length, pattern=pattern
818 )
819 elif t is bytes:
820 return BytesType(min_length=min_length, max_length=max_length)
821 elif t is bytearray:
822 return ByteArrayType(min_length=min_length, max_length=max_length)
823 elif t is memoryview:
824 return MemoryViewType(min_length=min_length, max_length=max_length)
825 elif t is datetime.datetime:
826 return DateTimeType(tz=tz)
827 elif t is datetime.time:
828 return TimeType(tz=tz)
829 elif t is datetime.date:
830 return DateType()
831 elif t is datetime.timedelta:
832 return TimeDeltaType()
833 elif t is uuid.UUID:
834 return UUIDType()
835 elif t is decimal.Decimal:
836 return DecimalType()
837 elif t is msgspec.Raw:
838 return RawType()
839 elif t is msgspec.msgpack.Ext:
840 return ExtType()
841 elif t is list:
842 return ListType(
843 self.translate(args[0]) if args else AnyType(),
844 min_length=min_length,
845 max_length=max_length,
846 )
847 elif t is set:
848 return SetType(
849 self.translate(args[0]) if args else AnyType(),
850 min_length=min_length,
851 max_length=max_length,
852 )
853 elif t is frozenset:
854 return FrozenSetType(
855 self.translate(args[0]) if args else AnyType(),
856 min_length=min_length,
857 max_length=max_length,
858 )
859 elif t is tuple:
860 # Handle an annoying compatibility issue:
861 # - Tuple[()] has args == ((),)
862 # - tuple[()] has args == ()
863 if args == ((),):
864 args = ()
865 if args is None:
866 return VarTupleType(
867 AnyType(), min_length=min_length, max_length=max_length
868 )
869 elif len(args) == 2 and args[-1] is ...:
870 return VarTupleType(
871 self.translate(args[0]),
872 min_length=min_length,
873 max_length=max_length,
874 )
875 else:
876 return TupleType(tuple(self.translate(a) for a in args))
877 elif t is dict:
878 return DictType(
879 self.translate(args[0]) if args else AnyType(),
880 self.translate(args[1]) if args else AnyType(),
881 min_length=min_length,
882 max_length=max_length,
883 )
884 elif t is Union:
885 args = tuple(self.translate(a) for a in args if a is not _UnsetType)
886 return args[0] if len(args) == 1 else UnionType(args)
887 elif t is Literal:
888 return LiteralType(tuple(sorted(args)))
889 elif _is_enum(t):
890 return EnumType(t)
891 elif is_struct_type(t):
892 cls = t[args] if args else t
893 if cls in self.cache:
894 return self.cache[cls]
895 config = t.__struct_config__
896 self.cache[cls] = out = StructType(
897 cls,
898 (),
899 tag_field=config.tag_field,
900 tag=config.tag,
901 array_like=config.array_like,
902 forbid_unknown_fields=config.forbid_unknown_fields,
903 )
905 hints = self._get_class_annotations(cls)
906 npos = len(t.__struct_fields__) - len(t.__struct_defaults__)
907 fields = []
908 for name, encode_name, default_obj in zip(
909 t.__struct_fields__,
910 t.__struct_encode_fields__,
911 (NODEFAULT,) * npos + t.__struct_defaults__,
912 ):
913 if default_obj is NODEFAULT:
914 required = True
915 default = default_factory = NODEFAULT
916 elif isinstance(default_obj, _Factory):
917 required = False
918 default = NODEFAULT
919 default_factory = default_obj.factory
920 else:
921 required = False
922 default = NODEFAULT if default_obj is UNSET else default_obj
923 default_factory = NODEFAULT
925 field = Field(
926 name=name,
927 encode_name=encode_name,
928 type=self.translate(hints[name]),
929 required=required,
930 default=default,
931 default_factory=default_factory,
932 )
933 fields.append(field)
935 out.fields = tuple(fields)
936 return out
937 elif _is_typeddict(t):
938 cls = t[args] if args else t
939 if cls in self.cache:
940 return self.cache[cls]
941 self.cache[cls] = out = TypedDictType(cls, ())
942 hints, required = _get_typeddict_info(cls)
943 out.fields = tuple(
944 Field(
945 name=name,
946 encode_name=name,
947 type=self.translate(field_type),
948 required=name in required,
949 )
950 for name, field_type in sorted(hints.items())
951 )
952 return out
953 elif _is_dataclass(t) or _is_attrs(t):
954 cls = t[args] if args else t
955 if cls in self.cache:
956 return self.cache[cls]
957 self.cache[cls] = out = DataclassType(cls, ())
958 _, info, defaults, _, _ = _get_dataclass_info(cls)
959 defaults = ((NODEFAULT,) * (len(info) - len(defaults))) + defaults
960 fields = []
961 for (name, typ, is_factory), default_obj in zip(info, defaults):
962 if default_obj is NODEFAULT:
963 required = True
964 default = default_factory = NODEFAULT
965 elif is_factory:
966 required = False
967 default = NODEFAULT
968 default_factory = default_obj
969 else:
970 required = False
971 default = NODEFAULT if default_obj is UNSET else default_obj
972 default_factory = NODEFAULT
974 fields.append(
975 Field(
976 name=name,
977 encode_name=name,
978 type=self.translate(typ),
979 required=required,
980 default=default,
981 default_factory=default_factory,
982 )
983 )
984 out.fields = tuple(fields)
985 return out
986 elif _is_namedtuple(t):
987 cls = t[args] if args else t
988 if cls in self.cache:
989 return self.cache[cls]
990 self.cache[cls] = out = NamedTupleType(cls, ())
991 hints = self._get_class_annotations(cls)
992 out.fields = tuple(
993 Field(
994 name=name,
995 encode_name=name,
996 type=self.translate(hints.get(name, Any)),
997 required=name not in t._field_defaults,
998 default=t._field_defaults.get(name, NODEFAULT),
999 )
1000 for name in t._fields
1001 )
1002 return out
1003 else:
1004 return CustomType(t)