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

353 statements  

1from __future__ import annotations 

2 

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 

11 

12try: 

13 from typing import TypeAliasType as _TypeAliasType # type: ignore 

14except Exception: 

15 _TypeAliasType = type("TypeAliasType", (), {}) # type: ignore 

16 

17import msgspec 

18from msgspec import NODEFAULT, UNSET, UnsetType as _UnsetType 

19 

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) 

32 

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) 

75 

76_PY315_PLUS = sys.version_info >= (3, 15) 

77 

78 

79def __dir__(): 

80 return __all__ 

81 

82 

83class Type(msgspec.Struct): 

84 """The base Type.""" 

85 

86 

87class Metadata(Type): 

88 """A type wrapping a subtype with additional metadata. 

89 

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 """ 

100 

101 type: Type 

102 extra_json_schema: dict | None = None 

103 extra: dict | None = None 

104 

105 

106class AnyType(Type): 

107 """A type corresponding to `typing.Any`.""" 

108 

109 

110class NoneType(Type): 

111 """A type corresponding to `None`.""" 

112 

113 

114class BoolType(Type): 

115 """A type corresponding to `bool`.""" 

116 

117 

118class IntType(Type): 

119 """A type corresponding to `int`. 

120 

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 """ 

134 

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 

140 

141 

142class FloatType(Type): 

143 """A type corresponding to `float`. 

144 

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 """ 

158 

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 

164 

165 

166class StrType(Type): 

167 """A type corresponding to `str`. 

168 

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 """ 

181 

182 min_length: int | None = None 

183 max_length: int | None = None 

184 pattern: str | None = None 

185 

186 

187class BytesType(Type): 

188 """A type corresponding to `bytes`. 

189 

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 """ 

199 

200 min_length: int | None = None 

201 max_length: int | None = None 

202 

203 

204class ByteArrayType(Type): 

205 """A type corresponding to `bytearray`. 

206 

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 """ 

216 

217 min_length: int | None = None 

218 max_length: int | None = None 

219 

220 

221class MemoryViewType(Type): 

222 """A type corresponding to `memoryview`. 

223 

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 """ 

233 

234 min_length: int | None = None 

235 max_length: int | None = None 

236 

237 

238class DateTimeType(Type): 

239 """A type corresponding to `datetime.datetime`. 

240 

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 """ 

249 

250 tz: bool | None = None 

251 

252 

253class TimeType(Type): 

254 """A type corresponding to `datetime.time`. 

255 

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 """ 

264 

265 tz: bool | None = None 

266 

267 

268class DateType(Type): 

269 """A type corresponding to `datetime.date`.""" 

270 

271 

272class TimeDeltaType(Type): 

273 """A type corresponding to `datetime.timedelta`.""" 

274 

275 

276class UUIDType(Type): 

277 """A type corresponding to `uuid.UUID`.""" 

278 

279 

280class DecimalType(Type): 

281 """A type corresponding to `decimal.Decimal`.""" 

282 

283 

284class ExtType(Type): 

285 """A type corresponding to `msgspec.msgpack.Ext`.""" 

286 

287 

288class RawType(Type): 

289 """A type corresponding to `msgspec.Raw`.""" 

290 

291 

292class EnumType(Type): 

293 """A type corresponding to an `enum.Enum` type. 

294 

295 Parameters 

296 ---------- 

297 cls: type 

298 The corresponding `enum.Enum` type. 

299 """ 

300 

301 cls: type[enum.Enum] 

302 

303 

304class LiteralType(Type): 

305 """A type corresponding to a `typing.Literal` type. 

306 

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 """ 

314 

315 values: tuple[bool | str | int | None, ...] 

316 

317 

318class CustomType(Type): 

319 """A custom type. 

320 

321 Parameters 

322 ---------- 

323 cls: type 

324 The corresponding custom type. 

325 """ 

326 

327 cls: type 

328 

329 

330class UnionType(Type): 

331 """A union type. 

332 

333 Parameters 

334 ---------- 

335 types: tuple[type, ...] 

336 A tuple of possible types for this union. 

337 """ 

338 

339 types: tuple[Type, ...] 

340 

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) 

345 

346 

347class CollectionType(Type): 

348 """A collection type. 

349 

350 This is the base type shared by collection types like `ListType`, 

351 `SetType`, etc. 

352 

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 """ 

364 

365 item_type: Type 

366 min_length: int | None = None 

367 max_length: int | None = None 

368 

369 

370class ListType(CollectionType): 

371 """A type corresponding to a `list`. 

372 

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 """ 

384 

385 

386class VarTupleType(CollectionType): 

387 """A type corresponding to a variadic `tuple`. 

388 

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 """ 

400 

401 

402class SetType(CollectionType): 

403 """A type corresponding to a `set`. 

404 

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 """ 

416 

417 

418class FrozenSetType(CollectionType): 

419 """A type corresponding to a `frozenset`. 

420 

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 """ 

432 

433 

434class TupleType(Type): 

435 """A type corresponding to `tuple`. 

436 

437 Parameters 

438 ---------- 

439 item_types: tuple[Type, ...] 

440 A tuple of types for each element in the tuple. 

441 """ 

442 

443 item_types: tuple[Type, ...] 

444 

445 

446class DictType(Type): 

447 """A type corresponding to `dict`. 

448 

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 """ 

462 

463 key_type: Type 

464 value_type: Type 

465 min_length: int | None = None 

466 max_length: int | None = None 

467 

468 

469class FrozenDictType(Type): 

470 """A type corresponding to `frozendict`. 

471 

472 Can only be emitted on Python 3.15+. 

473 

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 """ 

487 

488 key_type: Type 

489 value_type: Type 

490 min_length: Union[int, None] = None 

491 max_length: Union[int, None] = None 

492 

493 

494class Field(msgspec.Struct): 

495 """A record describing a field in an object-like type. 

496 

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 """ 

517 

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) 

524 

525 

526class TypedDictType(Type): 

527 """A type corresponding to a `typing.TypedDict` type. 

528 

529 Parameters 

530 ---------- 

531 cls: type 

532 The corresponding TypedDict type. 

533 fields: tuple[Field, ...] 

534 A tuple of fields in the TypedDict. 

535 """ 

536 

537 cls: type 

538 fields: tuple[Field, ...] 

539 

540 

541class NamedTupleType(Type): 

542 """A type corresponding to a `typing.NamedTuple` type. 

543 

544 Parameters 

545 ---------- 

546 cls: type 

547 The corresponding NamedTuple type. 

548 fields: tuple[Field, ...] 

549 A tuple of fields in the NamedTuple. 

550 """ 

551 

552 cls: type 

553 fields: tuple[Field, ...] 

554 

555 

556class DataclassType(Type): 

557 """A type corresponding to a `dataclasses` or `attrs` type. 

558 

559 Parameters 

560 ---------- 

561 cls: type 

562 The corresponding dataclass type. 

563 fields: tuple[Field, ...] 

564 A tuple of fields in the dataclass. 

565 """ 

566 

567 cls: type 

568 fields: tuple[Field, ...] 

569 

570 

571class StructType(Type): 

572 """A type corresponding to a `msgspec.Struct` type. 

573 

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 """ 

590 

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 

597 

598 

599def multi_type_info(types: Iterable[Any]) -> tuple[Type, ...]: 

600 """Get information about multiple msgspec-compatible types. 

601 

602 Parameters 

603 ---------- 

604 types: an iterable of types 

605 The types to get info about. 

606 

607 Returns 

608 ------- 

609 tuple[Type, ...] 

610 

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() 

620 

621 

622def type_info(type: Any) -> Type: 

623 """Get information about a msgspec-compatible type. 

624 

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. 

628 

629 Parameters 

630 ---------- 

631 type: type 

632 The type to get info about. 

633 

634 Returns 

635 ------- 

636 Type 

637 

638 Examples 

639 -------- 

640 >>> msgspec.inspect.type_info(bool) 

641 BoolType() 

642 

643 >>> msgspec.inspect.type_info(int) 

644 IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None) 

645 

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] 

651 

652 

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 

663 

664 if origin is not None: 

665 args = None 

666 break 

667 

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 

691 

692 if type(origin) is _types_UnionType: 

693 args = origin.__args__ 

694 origin = Union 

695 return origin, args, tuple(metadata) 

696 

697 

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) 

708 

709 

710def _is_enum(t): 

711 return type(t) is enum.EnumMeta 

712 

713 

714def _is_dataclass(t): 

715 return hasattr(t, "__dataclass_fields__") 

716 

717 

718def _is_attrs(t): 

719 return hasattr(t, "__attrs_attrs__") 

720 

721 

722def _is_typeddict(t): 

723 try: 

724 return issubclass(t, dict) and hasattr(t, "__total__") 

725 except TypeError: 

726 return False 

727 

728 

729def _is_namedtuple(t): 

730 try: 

731 return issubclass(t, tuple) and hasattr(t, "_fields") 

732 except TypeError: 

733 return False 

734 

735 

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 

753 

754 

755class _Translator: 

756 def __init__(self, types): 

757 self.types = tuple(types) 

758 self.type_hints = {} 

759 self.cache = {} 

760 

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 

768 

769 def run(self): 

770 # First construct a decoder to validate the types are valid 

771 from ._core import MsgpackDecoder 

772 

773 MsgpackDecoder(tuple[self.types]) 

774 return tuple(self.translate(t) for t in self.types) 

775 

776 def translate(self, typ): 

777 t, args, metadata = _origin_args_metadata(typ) 

778 

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) 

807 

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 

816 

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 ) 

941 

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 

961 

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) 

971 

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 

1010 

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)