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

342 statements  

1from __future__ import annotations 

2 

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) 

17 

18try: 

19 from types import UnionType as _types_UnionType # type: ignore 

20except Exception: 

21 _types_UnionType = type("UnionType", (), {}) # type: ignore 

22 

23try: 

24 from typing import TypeAliasType as _TypeAliasType # type: ignore 

25except Exception: 

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

27 

28import msgspec 

29from msgspec import NODEFAULT, UNSET, UnsetType as _UnsetType 

30 

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) 

43 

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) 

85 

86 

87def __dir__(): 

88 return __all__ 

89 

90 

91class Type(msgspec.Struct): 

92 """The base Type.""" 

93 

94 

95class Metadata(Type): 

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

97 

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

108 

109 type: Type 

110 extra_json_schema: Union[dict, None] = None 

111 extra: Union[dict, None] = None 

112 

113 

114class AnyType(Type): 

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

116 

117 

118class NoneType(Type): 

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

120 

121 

122class BoolType(Type): 

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

124 

125 

126class IntType(Type): 

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

128 

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

142 

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 

148 

149 

150class FloatType(Type): 

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

152 

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

166 

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 

172 

173 

174class StrType(Type): 

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

176 

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

189 

190 min_length: Union[int, None] = None 

191 max_length: Union[int, None] = None 

192 pattern: Union[str, None] = None 

193 

194 

195class BytesType(Type): 

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

197 

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

207 

208 min_length: Union[int, None] = None 

209 max_length: Union[int, None] = None 

210 

211 

212class ByteArrayType(Type): 

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

214 

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

224 

225 min_length: Union[int, None] = None 

226 max_length: Union[int, None] = None 

227 

228 

229class MemoryViewType(Type): 

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

231 

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

241 

242 min_length: Union[int, None] = None 

243 max_length: Union[int, None] = None 

244 

245 

246class DateTimeType(Type): 

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

248 

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

257 

258 tz: Union[bool, None] = None 

259 

260 

261class TimeType(Type): 

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

263 

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

272 

273 tz: Union[bool, None] = None 

274 

275 

276class DateType(Type): 

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

278 

279 

280class TimeDeltaType(Type): 

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

282 

283 

284class UUIDType(Type): 

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

286 

287 

288class DecimalType(Type): 

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

290 

291 

292class ExtType(Type): 

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

294 

295 

296class RawType(Type): 

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

298 

299 

300class EnumType(Type): 

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

302 

303 Parameters 

304 ---------- 

305 cls: type 

306 The corresponding `enum.Enum` type. 

307 """ 

308 

309 cls: typing_Type[enum.Enum] 

310 

311 

312class LiteralType(Type): 

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

314 

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

321 

322 values: Union[Tuple[str, ...], Tuple[int, ...]] 

323 

324 

325class CustomType(Type): 

326 """A custom type. 

327 

328 Parameters 

329 ---------- 

330 cls: type 

331 The corresponding custom type. 

332 """ 

333 

334 cls: type 

335 

336 

337class UnionType(Type): 

338 """A union type. 

339 

340 Parameters 

341 ---------- 

342 types: Tuple[Type, ...] 

343 A tuple of possible types for this union. 

344 """ 

345 

346 types: Tuple[Type, ...] 

347 

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) 

352 

353 

354class CollectionType(Type): 

355 """A collection type. 

356 

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

358 `SetType`, etc. 

359 

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

371 

372 item_type: Type 

373 min_length: Union[int, None] = None 

374 max_length: Union[int, None] = None 

375 

376 

377class ListType(CollectionType): 

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

379 

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

391 

392 

393class VarTupleType(CollectionType): 

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

395 

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

407 

408 

409class SetType(CollectionType): 

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

411 

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

423 

424 

425class FrozenSetType(CollectionType): 

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

427 

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

439 

440 

441class TupleType(Type): 

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

443 

444 Parameters 

445 ---------- 

446 item_types: Tuple[Type, ...] 

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

448 """ 

449 

450 item_types: Tuple[Type, ...] 

451 

452 

453class DictType(Type): 

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

455 

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

469 

470 key_type: Type 

471 value_type: Type 

472 min_length: Union[int, None] = None 

473 max_length: Union[int, None] = None 

474 

475 

476class Field(msgspec.Struct): 

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

478 

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

499 

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) 

506 

507 

508class TypedDictType(Type): 

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

510 

511 Parameters 

512 ---------- 

513 cls: type 

514 The corresponding TypedDict type. 

515 fields: Tuple[Field, ...] 

516 A tuple of fields in the TypedDict. 

517 """ 

518 

519 cls: type 

520 fields: Tuple[Field, ...] 

521 

522 

523class NamedTupleType(Type): 

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

525 

526 Parameters 

527 ---------- 

528 cls: type 

529 The corresponding NamedTuple type. 

530 fields: Tuple[Field, ...] 

531 A tuple of fields in the NamedTuple. 

532 """ 

533 

534 cls: type 

535 fields: Tuple[Field, ...] 

536 

537 

538class DataclassType(Type): 

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

540 

541 Parameters 

542 ---------- 

543 cls: type 

544 The corresponding dataclass type. 

545 fields: Tuple[Field, ...] 

546 A tuple of fields in the dataclass. 

547 """ 

548 

549 cls: type 

550 fields: Tuple[Field, ...] 

551 

552 

553class StructType(Type): 

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

555 

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

572 

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 

579 

580 

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

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

583 

584 Parameters 

585 ---------- 

586 types: an iterable of types 

587 The types to get info about. 

588 

589 Returns 

590 ------- 

591 tuple[Type, ...] 

592 

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

602 

603 

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

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

606 

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. 

610 

611 Parameters 

612 ---------- 

613 type: type 

614 The type to get info about. 

615 

616 Returns 

617 ------- 

618 Type 

619 

620 Examples 

621 -------- 

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

623 BoolType() 

624 

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

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

627 

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] 

633 

634 

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 

645 

646 if origin is not None: 

647 args = None 

648 break 

649 

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 

673 

674 if type(origin) is _types_UnionType: 

675 args = origin.__args__ 

676 origin = Union 

677 return origin, args, tuple(metadata) 

678 

679 

680def _is_enum(t): 

681 return type(t) is enum.EnumMeta 

682 

683 

684def _is_dataclass(t): 

685 return hasattr(t, "__dataclass_fields__") 

686 

687 

688def _is_attrs(t): 

689 return hasattr(t, "__attrs_attrs__") 

690 

691 

692def _is_typeddict(t): 

693 try: 

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

695 except TypeError: 

696 return False 

697 

698 

699def _is_namedtuple(t): 

700 try: 

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

702 except TypeError: 

703 return False 

704 

705 

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 

723 

724 

725class _Translator: 

726 def __init__(self, types): 

727 self.types = tuple(types) 

728 self.type_hints = {} 

729 self.cache = {} 

730 

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 

738 

739 def run(self): 

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

741 from ._core import MsgpackDecoder 

742 

743 MsgpackDecoder(Tuple[self.types]) 

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

745 

746 def translate(self, typ): 

747 t, args, metadata = _origin_args_metadata(typ) 

748 

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) 

777 

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 

786 

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 ) 

904 

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 

924 

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) 

934 

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 

973 

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)