Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/converters.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

535 statements  

1from __future__ import annotations 

2 

3from collections import Counter, deque 

4from collections.abc import Callable, Iterable 

5from collections.abc import Mapping as AbcMapping 

6from collections.abc import MutableMapping as AbcMutableMapping 

7from dataclasses import Field 

8from enum import Enum 

9from inspect import Signature 

10from inspect import signature as inspect_signature 

11from pathlib import Path 

12from typing import Any, Optional, Tuple, TypeVar, overload 

13 

14from attrs import Attribute, resolve_types 

15from attrs import has as attrs_has 

16from typing_extensions import Self 

17 

18from ._compat import ( 

19 ANIES, 

20 FrozenSetSubscriptable, 

21 Mapping, 

22 MutableMapping, 

23 MutableSequence, 

24 NoneType, 

25 OriginAbstractSet, 

26 OriginMutableSet, 

27 Sequence, 

28 Set, 

29 TypeAlias, 

30 fields, 

31 get_final_base, 

32 get_newtype_base, 

33 get_origin, 

34 has, 

35 has_with_generic, 

36 is_annotated, 

37 is_bare, 

38 is_counter, 

39 is_deque, 

40 is_frozenset, 

41 is_generic, 

42 is_generic_attrs, 

43 is_hetero_tuple, 

44 is_literal, 

45 is_mapping, 

46 is_mutable_set, 

47 is_optional, 

48 is_protocol, 

49 is_sequence, 

50 is_tuple, 

51 is_typeddict, 

52 is_union_type, 

53 signature, 

54) 

55from .cols import ( 

56 defaultdict_structure_factory, 

57 is_defaultdict, 

58 is_namedtuple, 

59 iterable_unstructure_factory, 

60 list_structure_factory, 

61 mapping_structure_factory, 

62 mapping_unstructure_factory, 

63 namedtuple_structure_factory, 

64 namedtuple_unstructure_factory, 

65) 

66from .disambiguators import create_default_dis_func, is_supported_union 

67from .dispatch import ( 

68 HookFactory, 

69 MultiStrategyDispatch, 

70 StructuredValue, 

71 StructureHook, 

72 TargetType, 

73 UnstructuredValue, 

74 UnstructureHook, 

75) 

76from .errors import ( 

77 IterableValidationError, 

78 IterableValidationNote, 

79 StructureHandlerNotFoundError, 

80) 

81from .fns import Predicate, identity, raise_error 

82from .gen import ( 

83 AttributeOverride, 

84 HeteroTupleUnstructureFn, 

85 IterableUnstructureFn, 

86 MappingUnstructureFn, 

87 make_dict_structure_fn, 

88 make_dict_unstructure_fn, 

89 make_hetero_tuple_unstructure_fn, 

90) 

91from .gen.typeddicts import make_dict_structure_fn as make_typeddict_dict_struct_fn 

92from .gen.typeddicts import make_dict_unstructure_fn as make_typeddict_dict_unstruct_fn 

93from .literals import is_literal_containing_enums 

94from .typealiases import ( 

95 get_type_alias_base, 

96 is_type_alias, 

97 type_alias_structure_factory, 

98) 

99from .types import SimpleStructureHook 

100 

101__all__ = ["BaseConverter", "Converter", "GenConverter", "UnstructureStrategy"] 

102 

103T = TypeVar("T") 

104V = TypeVar("V") 

105 

106UnstructureHookFactory = TypeVar( 

107 "UnstructureHookFactory", bound=HookFactory[UnstructureHook] 

108) 

109 

110# The Extended factory also takes a converter. 

111ExtendedUnstructureHookFactory: TypeAlias = Callable[[TargetType, T], UnstructureHook] 

112 

113# This typevar for the BaseConverter. 

114AnyUnstructureHookFactoryBase = TypeVar( 

115 "AnyUnstructureHookFactoryBase", 

116 bound="HookFactory[UnstructureHook] | ExtendedUnstructureHookFactory[BaseConverter]", 

117) 

118 

119# This typevar for the Converter. 

120AnyUnstructureHookFactory = TypeVar( 

121 "AnyUnstructureHookFactory", 

122 bound="HookFactory[UnstructureHook] | ExtendedUnstructureHookFactory[Converter]", 

123) 

124 

125StructureHookFactory = TypeVar("StructureHookFactory", bound=HookFactory[StructureHook]) 

126 

127# The Extended factory also takes a converter. 

128ExtendedStructureHookFactory: TypeAlias = Callable[[TargetType, T], StructureHook] 

129 

130# This typevar for the BaseConverter. 

131AnyStructureHookFactoryBase = TypeVar( 

132 "AnyStructureHookFactoryBase", 

133 bound="HookFactory[StructureHook] | ExtendedStructureHookFactory[BaseConverter]", 

134) 

135 

136# This typevar for the Converter. 

137AnyStructureHookFactory = TypeVar( 

138 "AnyStructureHookFactory", 

139 bound="HookFactory[StructureHook] | ExtendedStructureHookFactory[Converter]", 

140) 

141 

142UnstructureHookT = TypeVar("UnstructureHookT", bound=UnstructureHook) 

143StructureHookT = TypeVar("StructureHookT", bound=StructureHook) 

144CounterT = TypeVar("CounterT", bound=Counter) 

145 

146 

147class UnstructureStrategy(Enum): 

148 """`attrs` classes unstructuring strategies.""" 

149 

150 AS_DICT = "asdict" 

151 AS_TUPLE = "astuple" 

152 

153 

154def _is_extended_factory(factory: Callable) -> bool: 

155 """Does this factory also accept a converter arg?""" 

156 # We use the original `inspect.signature` to not evaluate string 

157 # annotations. 

158 sig = inspect_signature(factory) 

159 return ( 

160 len(sig.parameters) >= 2 

161 and (list(sig.parameters.values())[1]).default is Signature.empty 

162 ) 

163 

164 

165class BaseConverter: 

166 """Converts between structured and unstructured data.""" 

167 

168 __slots__ = ( 

169 "_dict_factory", 

170 "_prefer_attrib_converters", 

171 "_struct_copy_skip", 

172 "_structure_attrs", 

173 "_structure_func", 

174 "_union_struct_registry", 

175 "_unstruct_copy_skip", 

176 "_unstructure_attrs", 

177 "_unstructure_func", 

178 "detailed_validation", 

179 ) 

180 

181 def __init__( 

182 self, 

183 dict_factory: Callable[[], Any] = dict, 

184 unstruct_strat: UnstructureStrategy = UnstructureStrategy.AS_DICT, 

185 prefer_attrib_converters: bool = False, 

186 detailed_validation: bool = True, 

187 unstructure_fallback_factory: HookFactory[UnstructureHook] = lambda _: identity, 

188 structure_fallback_factory: HookFactory[StructureHook] = lambda t: raise_error( 

189 None, t 

190 ), 

191 ) -> None: 

192 """ 

193 :param detailed_validation: Whether to use a slightly slower mode for detailed 

194 validation errors. 

195 :param unstructure_fallback_factory: A hook factory to be called when no 

196 registered unstructuring hooks match. 

197 :param structure_fallback_factory: A hook factory to be called when no 

198 registered structuring hooks match. 

199 

200 .. versionadded:: 23.2.0 *unstructure_fallback_factory* 

201 .. versionadded:: 23.2.0 *structure_fallback_factory* 

202 .. versionchanged:: 24.2.0 

203 The default `structure_fallback_factory` now raises errors for missing handlers 

204 more eagerly, surfacing problems earlier. 

205 """ 

206 unstruct_strat = UnstructureStrategy(unstruct_strat) 

207 self._prefer_attrib_converters = prefer_attrib_converters 

208 

209 self.detailed_validation = detailed_validation 

210 self._union_struct_registry: dict[Any, Callable[[Any, type[T]], T]] = {} 

211 

212 # Create a per-instance cache. 

213 if unstruct_strat is UnstructureStrategy.AS_DICT: 

214 self._unstructure_attrs = self.unstructure_attrs_asdict 

215 self._structure_attrs = self.structure_attrs_fromdict 

216 else: 

217 self._unstructure_attrs = self.unstructure_attrs_astuple 

218 self._structure_attrs = self.structure_attrs_fromtuple 

219 

220 self._unstructure_func = MultiStrategyDispatch( 

221 unstructure_fallback_factory, self 

222 ) 

223 self._unstructure_func.register_cls_list( 

224 [(bytes, identity), (str, identity), (Path, str)] 

225 ) 

226 self._unstructure_func.register_func_list( 

227 [ 

228 ( 

229 is_protocol, 

230 lambda o: self.unstructure(o, unstructure_as=o.__class__), 

231 ), 

232 ( 

233 lambda t: get_final_base(t) is not None, 

234 lambda t: self.get_unstructure_hook(get_final_base(t)), 

235 True, 

236 ), 

237 ( 

238 is_type_alias, 

239 lambda t: self.get_unstructure_hook(get_type_alias_base(t)), 

240 True, 

241 ), 

242 (is_literal_containing_enums, self.unstructure), 

243 (is_mapping, self._unstructure_mapping), 

244 (is_sequence, self._unstructure_seq), 

245 (is_mutable_set, self._unstructure_seq), 

246 (is_frozenset, self._unstructure_seq), 

247 (lambda t: issubclass(t, Enum), self._unstructure_enum), 

248 (has, self._unstructure_attrs), 

249 (is_union_type, self._unstructure_union), 

250 (lambda t: t in ANIES, self.unstructure), 

251 ] 

252 ) 

253 

254 # Per-instance register of to-attrs converters. 

255 # Singledispatch dispatches based on the first argument, so we 

256 # store the function and switch the arguments in self.loads. 

257 self._structure_func = MultiStrategyDispatch(structure_fallback_factory, self) 

258 self._structure_func.register_func_list( 

259 [ 

260 ( 

261 lambda cl: cl in ANIES or cl is Optional or cl is None, 

262 lambda v, _: v, 

263 ), 

264 (is_generic_attrs, self._gen_structure_generic, True), 

265 (lambda t: get_newtype_base(t) is not None, self._structure_newtype), 

266 (is_type_alias, type_alias_structure_factory, "extended"), 

267 ( 

268 lambda t: get_final_base(t) is not None, 

269 self._structure_final_factory, 

270 True, 

271 ), 

272 (is_literal, self._structure_simple_literal), 

273 (is_literal_containing_enums, self._structure_enum_literal), 

274 (is_sequence, list_structure_factory, "extended"), 

275 (is_deque, self._structure_deque), 

276 (is_mutable_set, self._structure_set), 

277 (is_frozenset, self._structure_frozenset), 

278 (is_tuple, self._structure_tuple), 

279 (is_namedtuple, namedtuple_structure_factory, "extended"), 

280 (is_mapping, self._structure_dict), 

281 (is_supported_union, self._gen_attrs_union_structure, True), 

282 (is_optional, self._structure_optional), 

283 ( 

284 lambda t: is_union_type(t) and t in self._union_struct_registry, 

285 self._union_struct_registry.__getitem__, 

286 True, 

287 ), 

288 (has, self._structure_attrs), 

289 ] 

290 ) 

291 # Strings are sequences. 

292 self._structure_func.register_cls_list( 

293 [ 

294 (str, self._structure_call), 

295 (bytes, self._structure_call), 

296 (int, self._structure_call), 

297 (float, self._structure_call), 

298 (Enum, self._structure_call), 

299 (Path, self._structure_call), 

300 ] 

301 ) 

302 

303 self._dict_factory = dict_factory 

304 

305 self._unstruct_copy_skip = self._unstructure_func.get_num_fns() 

306 self._struct_copy_skip = self._structure_func.get_num_fns() 

307 

308 def unstructure(self, obj: Any, unstructure_as: Any = None) -> Any: 

309 return self._unstructure_func.dispatch( 

310 obj.__class__ if unstructure_as is None else unstructure_as 

311 )(obj) 

312 

313 @property 

314 def unstruct_strat(self) -> UnstructureStrategy: 

315 """The default way of unstructuring ``attrs`` classes.""" 

316 return ( 

317 UnstructureStrategy.AS_DICT 

318 if self._unstructure_attrs == self.unstructure_attrs_asdict 

319 else UnstructureStrategy.AS_TUPLE 

320 ) 

321 

322 @overload 

323 def register_unstructure_hook(self, cls: UnstructureHookT) -> UnstructureHookT: ... 

324 

325 @overload 

326 def register_unstructure_hook(self, cls: Any, func: UnstructureHook) -> None: ... 

327 

328 def register_unstructure_hook( 

329 self, cls: Any = None, func: UnstructureHook | None = None 

330 ) -> Callable[[UnstructureHook]] | None: 

331 """Register a class-to-primitive converter function for a class. 

332 

333 The converter function should take an instance of the class and return 

334 its Python equivalent. 

335 

336 May also be used as a decorator. When used as a decorator, the first 

337 argument annotation from the decorated function will be used as the 

338 type to register the hook for. 

339 

340 .. versionchanged:: 24.1.0 

341 This method may now be used as a decorator. 

342 .. versionchanged:: 25.1.0 

343 Modern type aliases are now supported. 

344 """ 

345 if func is None: 

346 # Autodetecting decorator. 

347 func = cls 

348 sig = signature(func) 

349 cls = next(iter(sig.parameters.values())).annotation 

350 self.register_unstructure_hook(cls, func) 

351 

352 return func 

353 

354 if attrs_has(cls): 

355 resolve_types(cls) 

356 if is_union_type(cls): 

357 self._unstructure_func.register_func_list([(lambda t: t == cls, func)]) 

358 elif is_type_alias(cls): 

359 self._unstructure_func.register_func_list([(lambda t: t is cls, func)]) 

360 elif get_newtype_base(cls) is not None: 

361 # This is a newtype, so we handle it specially. 

362 self._unstructure_func.register_func_list([(lambda t: t is cls, func)]) 

363 else: 

364 self._unstructure_func.register_cls_list([(cls, func)]) 

365 return None 

366 

367 def register_unstructure_hook_func( 

368 self, check_func: Predicate, func: UnstructureHook 

369 ) -> None: 

370 """Register a class-to-primitive converter function for a class, using 

371 a function to check if it's a match. 

372 """ 

373 self._unstructure_func.register_func_list([(check_func, func)]) 

374 

375 @overload 

376 def register_unstructure_hook_factory( 

377 self, predicate: Predicate 

378 ) -> Callable[[AnyUnstructureHookFactoryBase], AnyUnstructureHookFactoryBase]: ... 

379 

380 @overload 

381 def register_unstructure_hook_factory( 

382 self, predicate: Predicate, factory: UnstructureHookFactory 

383 ) -> UnstructureHookFactory: ... 

384 

385 @overload 

386 def register_unstructure_hook_factory( 

387 self, 

388 predicate: Predicate, 

389 factory: ExtendedUnstructureHookFactory[BaseConverter], 

390 ) -> ExtendedUnstructureHookFactory[BaseConverter]: ... 

391 

392 def register_unstructure_hook_factory(self, predicate, factory=None): 

393 """ 

394 Register a hook factory for a given predicate. 

395 

396 The hook factory may expose an additional required parameter. In this case, 

397 the current converter will be provided to the hook factory as that 

398 parameter. 

399 

400 May also be used as a decorator. 

401 

402 :param predicate: A function that, given a type, returns whether the factory 

403 can produce a hook for that type. 

404 :param factory: A callable that, given a type, produces an unstructuring 

405 hook for that type. This unstructuring hook will be cached. 

406 

407 .. versionchanged:: 24.1.0 

408 This method may now be used as a decorator. 

409 The factory may also receive the converter as a second, required argument. 

410 """ 

411 if factory is None: 

412 

413 def decorator(factory): 

414 # Is this an extended factory (takes a converter too)? 

415 if _is_extended_factory(factory): 

416 self._unstructure_func.register_func_list( 

417 [(predicate, factory, "extended")] 

418 ) 

419 else: 

420 self._unstructure_func.register_func_list( 

421 [(predicate, factory, True)] 

422 ) 

423 

424 return decorator 

425 

426 self._unstructure_func.register_func_list( 

427 [ 

428 ( 

429 predicate, 

430 factory, 

431 "extended" if _is_extended_factory(factory) else True, 

432 ) 

433 ] 

434 ) 

435 return factory 

436 

437 def get_unstructure_hook( 

438 self, type: Any, cache_result: bool = True 

439 ) -> UnstructureHook: 

440 """Get the unstructure hook for the given type. 

441 

442 This hook can be manually called, or composed with other functions 

443 and re-registered. 

444 

445 If no hook is registered, the converter unstructure fallback factory 

446 will be used to produce one. 

447 

448 :param cache: Whether to cache the returned hook. 

449 

450 .. versionadded:: 24.1.0 

451 """ 

452 return ( 

453 self._unstructure_func.dispatch(type) 

454 if cache_result 

455 else self._unstructure_func.dispatch_without_caching(type) 

456 ) 

457 

458 @overload 

459 def register_structure_hook(self, cl: StructureHookT) -> StructureHookT: ... 

460 

461 @overload 

462 def register_structure_hook(self, cl: Any, func: StructureHook) -> None: ... 

463 

464 def register_structure_hook( 

465 self, cl: Any, func: StructureHook | None = None 

466 ) -> None: 

467 """Register a primitive-to-class converter function for a type. 

468 

469 The converter function should take two arguments: 

470 * a Python object to be converted, 

471 * the type to convert to 

472 

473 and return the instance of the class. The type may seem redundant, but 

474 is sometimes needed (for example, when dealing with generic classes). 

475 

476 This method may be used as a decorator. In this case, the decorated 

477 hook must have a return type annotation, and this annotation will be used 

478 as the type for the hook. 

479 

480 .. versionchanged:: 24.1.0 

481 This method may now be used as a decorator. 

482 .. versionchanged:: 25.1.0 

483 Modern type aliases are now supported. 

484 """ 

485 if func is None: 

486 # The autodetecting decorator. 

487 func = cl 

488 sig = signature(func) 

489 self.register_structure_hook(sig.return_annotation, func) 

490 return func 

491 

492 if attrs_has(cl): 

493 resolve_types(cl) 

494 if is_union_type(cl): 

495 self._union_struct_registry[cl] = func 

496 self._structure_func.clear_cache() 

497 elif is_type_alias(cl): 

498 # Type aliases are special-cased. 

499 self._structure_func.register_func_list([(lambda t: t is cl, func)]) 

500 elif get_newtype_base(cl) is not None: 

501 # This is a newtype, so we handle it specially. 

502 self._structure_func.register_func_list([(lambda t: t is cl, func)]) 

503 else: 

504 self._structure_func.register_cls_list([(cl, func)]) 

505 return None 

506 

507 def register_structure_hook_func( 

508 self, check_func: Predicate, func: StructureHook 

509 ) -> None: 

510 """Register a class-to-primitive converter function for a class, using 

511 a function to check if it's a match. 

512 """ 

513 self._structure_func.register_func_list([(check_func, func)]) 

514 

515 @overload 

516 def register_structure_hook_factory( 

517 self, predicate: Predicate 

518 ) -> Callable[[AnyStructureHookFactoryBase], AnyStructureHookFactoryBase]: ... 

519 

520 @overload 

521 def register_structure_hook_factory( 

522 self, predicate: Predicate, factory: StructureHookFactory 

523 ) -> StructureHookFactory: ... 

524 

525 @overload 

526 def register_structure_hook_factory( 

527 self, predicate: Predicate, factory: ExtendedStructureHookFactory[BaseConverter] 

528 ) -> ExtendedStructureHookFactory[BaseConverter]: ... 

529 

530 def register_structure_hook_factory(self, predicate, factory=None): 

531 """ 

532 Register a hook factory for a given predicate. 

533 

534 The hook factory may expose an additional required parameter. In this case, 

535 the current converter will be provided to the hook factory as that 

536 parameter. 

537 

538 May also be used as a decorator. 

539 

540 :param predicate: A function that, given a type, returns whether the factory 

541 can produce a hook for that type. 

542 :param factory: A callable that, given a type, produces a structuring 

543 hook for that type. This structuring hook will be cached. 

544 

545 .. versionchanged:: 24.1.0 

546 This method may now be used as a decorator. 

547 The factory may also receive the converter as a second, required argument. 

548 """ 

549 if factory is None: 

550 # Decorator use. 

551 def decorator(factory): 

552 # Is this an extended factory (takes a converter too)? 

553 if _is_extended_factory(factory): 

554 self._structure_func.register_func_list( 

555 [(predicate, factory, "extended")] 

556 ) 

557 else: 

558 self._structure_func.register_func_list( 

559 [(predicate, factory, True)] 

560 ) 

561 

562 return decorator 

563 self._structure_func.register_func_list( 

564 [ 

565 ( 

566 predicate, 

567 factory, 

568 "extended" if _is_extended_factory(factory) else True, 

569 ) 

570 ] 

571 ) 

572 return factory 

573 

574 def structure(self, obj: UnstructuredValue, cl: type[T]) -> T: 

575 """Convert unstructured Python data structures to structured data.""" 

576 return self._structure_func.dispatch(cl)(obj, cl) 

577 

578 def get_structure_hook(self, type: Any, cache_result: bool = True) -> StructureHook: 

579 """Get the structure hook for the given type. 

580 

581 This hook can be manually called, or composed with other functions 

582 and re-registered. 

583 

584 If no hook is registered, the converter structure fallback factory 

585 will be used to produce one. 

586 

587 :param cache: Whether to cache the returned hook. 

588 

589 .. versionadded:: 24.1.0 

590 """ 

591 return ( 

592 self._structure_func.dispatch(type) 

593 if cache_result 

594 else self._structure_func.dispatch_without_caching(type) 

595 ) 

596 

597 # Classes to Python primitives. 

598 def unstructure_attrs_asdict(self, obj: Any) -> dict[str, Any]: 

599 """Our version of `attrs.asdict`, so we can call back to us.""" 

600 attrs = fields(obj.__class__) 

601 dispatch = self._unstructure_func.dispatch 

602 rv = self._dict_factory() 

603 for a in attrs: 

604 name = a.name 

605 v = getattr(obj, name) 

606 rv[name] = dispatch(a.type or v.__class__)(v) 

607 return rv 

608 

609 def unstructure_attrs_astuple(self, obj: Any) -> tuple[Any, ...]: 

610 """Our version of `attrs.astuple`, so we can call back to us.""" 

611 attrs = fields(obj.__class__) 

612 dispatch = self._unstructure_func.dispatch 

613 res = [] 

614 for a in attrs: 

615 name = a.name 

616 v = getattr(obj, name) 

617 res.append(dispatch(a.type or v.__class__)(v)) 

618 return tuple(res) 

619 

620 def _unstructure_enum(self, obj: Enum) -> Any: 

621 """Convert an enum to its value.""" 

622 return obj.value 

623 

624 def _unstructure_seq(self, seq: Sequence[T]) -> Sequence[T]: 

625 """Convert a sequence to primitive equivalents.""" 

626 # We can reuse the sequence class, so tuples stay tuples. 

627 dispatch = self._unstructure_func.dispatch 

628 return seq.__class__(dispatch(e.__class__)(e) for e in seq) 

629 

630 def _unstructure_mapping(self, mapping: Mapping[T, V]) -> Mapping[T, V]: 

631 """Convert a mapping of attr classes to primitive equivalents.""" 

632 

633 # We can reuse the mapping class, so dicts stay dicts and OrderedDicts 

634 # stay OrderedDicts. 

635 dispatch = self._unstructure_func.dispatch 

636 return mapping.__class__( 

637 (dispatch(k.__class__)(k), dispatch(v.__class__)(v)) 

638 for k, v in mapping.items() 

639 ) 

640 

641 # note: Use UnionType when 3.11 is released as 

642 # the behaviour of @final is changed. This would 

643 # affect how we can support UnionType in ._compat.py 

644 def _unstructure_union(self, obj: Any) -> Any: 

645 """ 

646 Unstructure an object as a union. 

647 

648 By default, just unstructures the instance. 

649 """ 

650 return self._unstructure_func.dispatch(obj.__class__)(obj) 

651 

652 # Python primitives to classes. 

653 

654 def _gen_structure_generic( 

655 self, cl: type[T] 

656 ) -> SimpleStructureHook[Mapping[str, Any], T]: 

657 """Create and return a hook for structuring generics.""" 

658 return make_dict_structure_fn( 

659 cl, self, _cattrs_prefer_attrib_converters=self._prefer_attrib_converters 

660 ) 

661 

662 def _gen_attrs_union_structure( 

663 self, cl: Any, use_literals: bool = True 

664 ) -> Callable[[Any, type[T]], type[T] | None]: 

665 """ 

666 Generate a structuring function for a union of attrs classes (and maybe None). 

667 

668 :param use_literals: Whether to consider literal fields. 

669 """ 

670 dis_fn = self._get_dis_func(cl, use_literals=use_literals) 

671 has_none = NoneType in cl.__args__ 

672 

673 if has_none: 

674 

675 def structure_attrs_union(obj, _) -> cl: 

676 if obj is None: 

677 return None 

678 return self.structure(obj, dis_fn(obj)) 

679 

680 else: 

681 

682 def structure_attrs_union(obj, _): 

683 return self.structure(obj, dis_fn(obj)) 

684 

685 return structure_attrs_union 

686 

687 @staticmethod 

688 def _structure_call(obj: Any, cl: type[T]) -> Any: 

689 """Just call ``cl`` with the given ``obj``. 

690 

691 This is just an optimization on the ``_structure_default`` case, when 

692 we know we can skip the ``if`` s. Use for ``str``, ``bytes``, ``enum``, 

693 etc. 

694 """ 

695 return cl(obj) 

696 

697 @staticmethod 

698 def _structure_simple_literal(val, type): 

699 if val not in type.__args__: 

700 raise Exception(f"{val} not in literal {type}") 

701 return val 

702 

703 @staticmethod 

704 def _structure_enum_literal(val, type): 

705 vals = {(x.value if isinstance(x, Enum) else x): x for x in type.__args__} 

706 try: 

707 return vals[val] 

708 except KeyError: 

709 raise Exception(f"{val} not in literal {type}") from None 

710 

711 def _structure_newtype(self, val: UnstructuredValue, type) -> StructuredValue: 

712 base = get_newtype_base(type) 

713 return self.get_structure_hook(base)(val, base) 

714 

715 def _structure_final_factory(self, type): 

716 base = get_final_base(type) 

717 res = self.get_structure_hook(base) 

718 return lambda v, _, __base=base: res(v, __base) 

719 

720 # Attrs classes. 

721 

722 def structure_attrs_fromtuple(self, obj: tuple[Any, ...], cl: type[T]) -> T: 

723 """Load an attrs class from a sequence (tuple).""" 

724 conv_obj = [] # A list of converter parameters. 

725 for a, value in zip(fields(cl), obj): 

726 # We detect the type by the metadata. 

727 converted = self._structure_attribute(a, value) 

728 conv_obj.append(converted) 

729 

730 return cl(*conv_obj) 

731 

732 def _structure_attribute(self, a: Attribute | Field, value: Any) -> Any: 

733 """Handle an individual attrs attribute.""" 

734 type_ = a.type 

735 attrib_converter = getattr(a, "converter", None) 

736 if self._prefer_attrib_converters and attrib_converter: 

737 # A attrib converter is defined on this attribute, and 

738 # prefer_attrib_converters is set to give these priority over registered 

739 # structure hooks. So, pass through the raw value, which attrs will flow 

740 # into the converter 

741 return value 

742 if type_ is None: 

743 # No type metadata. 

744 return value 

745 

746 try: 

747 return self._structure_func.dispatch(type_)(value, type_) 

748 except StructureHandlerNotFoundError: 

749 if attrib_converter: 

750 # Return the original value and fallback to using an attrib converter. 

751 return value 

752 raise 

753 

754 def structure_attrs_fromdict(self, obj: Mapping[str, Any], cl: type[T]) -> T: 

755 """Instantiate an attrs class from a mapping (dict).""" 

756 # For public use. 

757 

758 conv_obj = {} # Start with a fresh dict, to ignore extra keys. 

759 for a in fields(cl): 

760 try: 

761 val = obj[a.name] 

762 except KeyError: 

763 continue 

764 

765 # try .alias and .name because this code also supports dataclasses! 

766 conv_obj[getattr(a, "alias", a.name)] = self._structure_attribute(a, val) 

767 

768 return cl(**conv_obj) 

769 

770 def _structure_deque(self, obj: Iterable[T], cl: Any) -> deque[T]: 

771 """Convert an iterable to a potentially generic deque.""" 

772 if is_bare(cl) or cl.__args__[0] in ANIES: 

773 res = deque(obj) 

774 else: 

775 elem_type = cl.__args__[0] 

776 handler = self._structure_func.dispatch(elem_type) 

777 if self.detailed_validation: 

778 errors = [] 

779 res = deque() 

780 ix = 0 # Avoid `enumerate` for performance. 

781 for e in obj: 

782 try: 

783 res.append(handler(e, elem_type)) 

784 except Exception as e: 

785 msg = IterableValidationNote( 

786 f"Structuring {cl} @ index {ix}", ix, elem_type 

787 ) 

788 e.__notes__ = [*getattr(e, "__notes__", []), msg] 

789 errors.append(e) 

790 finally: 

791 ix += 1 

792 if errors: 

793 raise IterableValidationError( 

794 f"While structuring {cl!r}", errors, cl 

795 ) 

796 else: 

797 res = deque(handler(e, elem_type) for e in obj) 

798 return res 

799 

800 def _structure_set( 

801 self, obj: Iterable[T], cl: Any, structure_to: type = set 

802 ) -> Set[T]: 

803 """Convert an iterable into a potentially generic set.""" 

804 if is_bare(cl) or cl.__args__[0] in ANIES: 

805 return structure_to(obj) 

806 elem_type = cl.__args__[0] 

807 handler = self._structure_func.dispatch(elem_type) 

808 if self.detailed_validation: 

809 errors = [] 

810 res = set() 

811 ix = 0 

812 for e in obj: 

813 try: 

814 res.add(handler(e, elem_type)) 

815 except Exception as exc: 

816 msg = IterableValidationNote( 

817 f"Structuring {structure_to.__name__} @ element {e!r}", 

818 ix, 

819 elem_type, 

820 ) 

821 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

822 errors.append(exc) 

823 finally: 

824 ix += 1 

825 if errors: 

826 raise IterableValidationError(f"While structuring {cl!r}", errors, cl) 

827 return res if structure_to is set else structure_to(res) 

828 if structure_to is set: 

829 return {handler(e, elem_type) for e in obj} 

830 return structure_to([handler(e, elem_type) for e in obj]) 

831 

832 def _structure_frozenset( 

833 self, obj: Iterable[T], cl: Any 

834 ) -> FrozenSetSubscriptable[T]: 

835 """Convert an iterable into a potentially generic frozenset.""" 

836 return self._structure_set(obj, cl, structure_to=frozenset) 

837 

838 def _structure_dict(self, obj: Mapping[T, V], cl: Any) -> dict[T, V]: 

839 """Convert a mapping into a potentially generic dict.""" 

840 if is_bare(cl) or cl.__args__ == (Any, Any): 

841 return dict(obj) 

842 key_type, val_type = cl.__args__ 

843 

844 if self.detailed_validation: 

845 key_handler = self._structure_func.dispatch(key_type) 

846 val_handler = self._structure_func.dispatch(val_type) 

847 errors = [] 

848 res = {} 

849 

850 for k, v in obj.items(): 

851 try: 

852 value = val_handler(v, val_type) 

853 except Exception as exc: 

854 msg = IterableValidationNote( 

855 f"Structuring mapping value @ key {k!r}", k, val_type 

856 ) 

857 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

858 errors.append(exc) 

859 continue 

860 

861 try: 

862 key = key_handler(k, key_type) 

863 res[key] = value 

864 except Exception as exc: 

865 msg = IterableValidationNote( 

866 f"Structuring mapping key @ key {k!r}", k, key_type 

867 ) 

868 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

869 errors.append(exc) 

870 

871 if errors: 

872 raise IterableValidationError(f"While structuring {cl!r}", errors, cl) 

873 return res 

874 

875 if key_type in ANIES: 

876 val_conv = self._structure_func.dispatch(val_type) 

877 return {k: val_conv(v, val_type) for k, v in obj.items()} 

878 if val_type in ANIES: 

879 key_conv = self._structure_func.dispatch(key_type) 

880 return {key_conv(k, key_type): v for k, v in obj.items()} 

881 key_conv = self._structure_func.dispatch(key_type) 

882 val_conv = self._structure_func.dispatch(val_type) 

883 return {key_conv(k, key_type): val_conv(v, val_type) for k, v in obj.items()} 

884 

885 def _structure_optional(self, obj, union): 

886 if obj is None: 

887 return None 

888 union_params = union.__args__ 

889 other = union_params[0] if union_params[1] is NoneType else union_params[1] 

890 # We can't actually have a Union of a Union, so this is safe. 

891 return self._structure_func.dispatch(other)(obj, other) 

892 

893 def _structure_tuple(self, obj: Iterable, tup: type[T]) -> T: 

894 """Deal with structuring into a tuple.""" 

895 tup_params = None if tup in (Tuple, tuple) else tup.__args__ 

896 has_ellipsis = tup_params and tup_params[-1] is Ellipsis 

897 if tup_params is None or (has_ellipsis and tup_params[0] in ANIES): 

898 # Just a Tuple. (No generic information.) 

899 return tuple(obj) 

900 if has_ellipsis: 

901 # We're dealing with a homogenous tuple, tuple[int, ...] 

902 tup_type = tup_params[0] 

903 conv = self._structure_func.dispatch(tup_type) 

904 if self.detailed_validation: 

905 errors = [] 

906 res = [] 

907 ix = 0 

908 for e in obj: 

909 try: 

910 res.append(conv(e, tup_type)) 

911 except Exception as exc: 

912 msg = IterableValidationNote( 

913 f"Structuring {tup} @ index {ix}", ix, tup_type 

914 ) 

915 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

916 errors.append(exc) 

917 finally: 

918 ix += 1 

919 if errors: 

920 raise IterableValidationError( 

921 f"While structuring {tup!r}", errors, tup 

922 ) 

923 return tuple(res) 

924 return tuple(conv(e, tup_type) for e in obj) 

925 

926 # We're dealing with a heterogenous tuple. 

927 exp_len = len(tup_params) 

928 if self.detailed_validation: 

929 errors = [] 

930 res = [] 

931 for ix, (t, e) in enumerate(zip(tup_params, obj)): 

932 try: 

933 conv = self._structure_func.dispatch(t) 

934 res.append(conv(e, t)) 

935 except Exception as exc: 

936 msg = IterableValidationNote( 

937 f"Structuring {tup} @ index {ix}", ix, t 

938 ) 

939 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

940 errors.append(exc) 

941 if len(obj) != exp_len: 

942 problem = "Not enough" if len(res) < exp_len else "Too many" 

943 exc = ValueError(f"{problem} values in {obj!r} to structure as {tup!r}") 

944 msg = f"Structuring {tup}" 

945 exc.__notes__ = [*getattr(exc, "__notes__", []), msg] 

946 errors.append(exc) 

947 if errors: 

948 raise IterableValidationError(f"While structuring {tup!r}", errors, tup) 

949 return tuple(res) 

950 

951 if len(obj) != exp_len: 

952 problem = "Not enough" if len(obj) < len(tup_params) else "Too many" 

953 raise ValueError(f"{problem} values in {obj!r} to structure as {tup!r}") 

954 return tuple( 

955 [self._structure_func.dispatch(t)(e, t) for t, e in zip(tup_params, obj)] 

956 ) 

957 

958 def _get_dis_func( 

959 self, 

960 union: Any, 

961 use_literals: bool = True, 

962 overrides: dict[str, AttributeOverride] | None = None, 

963 ) -> Callable[[Any], type]: 

964 """Fetch or try creating a disambiguation function for a union.""" 

965 union_types = union.__args__ 

966 if NoneType in union_types: 

967 # We support unions of attrs classes and NoneType higher in the 

968 # logic. 

969 union_types = tuple(e for e in union_types if e is not NoneType) 

970 

971 if not all(has(get_origin(e) or e) for e in union_types): 

972 raise StructureHandlerNotFoundError( 

973 "Only unions of attrs classes and dataclasses supported " 

974 "currently. Register a structure hook manually.", 

975 type_=union, 

976 ) 

977 

978 return create_default_dis_func( 

979 self, 

980 *union_types, 

981 use_literals=use_literals, 

982 overrides=overrides if overrides is not None else "from_converter", 

983 ) 

984 

985 def __deepcopy__(self, _) -> BaseConverter: 

986 return self.copy() 

987 

988 def copy( 

989 self, 

990 dict_factory: Callable[[], Any] | None = None, 

991 unstruct_strat: UnstructureStrategy | None = None, 

992 prefer_attrib_converters: bool | None = None, 

993 detailed_validation: bool | None = None, 

994 ) -> Self: 

995 """Create a copy of the converter, keeping all existing custom hooks. 

996 

997 :param detailed_validation: Whether to use a slightly slower mode for detailed 

998 validation errors. 

999 """ 

1000 res = self.__class__( 

1001 dict_factory if dict_factory is not None else self._dict_factory, 

1002 ( 

1003 unstruct_strat 

1004 if unstruct_strat is not None 

1005 else ( 

1006 UnstructureStrategy.AS_DICT 

1007 if self._unstructure_attrs == self.unstructure_attrs_asdict 

1008 else UnstructureStrategy.AS_TUPLE 

1009 ) 

1010 ), 

1011 ( 

1012 prefer_attrib_converters 

1013 if prefer_attrib_converters is not None 

1014 else self._prefer_attrib_converters 

1015 ), 

1016 ( 

1017 detailed_validation 

1018 if detailed_validation is not None 

1019 else self.detailed_validation 

1020 ), 

1021 ) 

1022 

1023 self._unstructure_func.copy_to(res._unstructure_func, self._unstruct_copy_skip) 

1024 self._structure_func.copy_to(res._structure_func, self._struct_copy_skip) 

1025 

1026 return res 

1027 

1028 

1029class Converter(BaseConverter): 

1030 """A converter which generates specialized un/structuring functions.""" 

1031 

1032 __slots__ = ( 

1033 "_unstruct_collection_overrides", 

1034 "forbid_extra_keys", 

1035 "omit_if_default", 

1036 "type_overrides", 

1037 ) 

1038 

1039 def __init__( 

1040 self, 

1041 dict_factory: Callable[[], Any] = dict, 

1042 unstruct_strat: UnstructureStrategy = UnstructureStrategy.AS_DICT, 

1043 omit_if_default: bool = False, 

1044 forbid_extra_keys: bool = False, 

1045 type_overrides: Mapping[type, AttributeOverride] = {}, 

1046 unstruct_collection_overrides: Mapping[type, UnstructureHook] = {}, 

1047 prefer_attrib_converters: bool = False, 

1048 detailed_validation: bool = True, 

1049 unstructure_fallback_factory: HookFactory[UnstructureHook] = lambda _: identity, 

1050 structure_fallback_factory: HookFactory[StructureHook] = lambda t: raise_error( 

1051 None, t 

1052 ), 

1053 ): 

1054 """ 

1055 :param detailed_validation: Whether to use a slightly slower mode for detailed 

1056 validation errors. 

1057 :param unstructure_fallback_factory: A hook factory to be called when no 

1058 registered unstructuring hooks match. 

1059 :param structure_fallback_factory: A hook factory to be called when no 

1060 registered structuring hooks match. 

1061 

1062 .. versionadded:: 23.2.0 *unstructure_fallback_factory* 

1063 .. versionadded:: 23.2.0 *structure_fallback_factory* 

1064 .. versionchanged:: 24.2.0 

1065 The default `structure_fallback_factory` now raises errors for missing handlers 

1066 more eagerly, surfacing problems earlier. 

1067 """ 

1068 super().__init__( 

1069 dict_factory=dict_factory, 

1070 unstruct_strat=unstruct_strat, 

1071 prefer_attrib_converters=prefer_attrib_converters, 

1072 detailed_validation=detailed_validation, 

1073 unstructure_fallback_factory=unstructure_fallback_factory, 

1074 structure_fallback_factory=structure_fallback_factory, 

1075 ) 

1076 self.omit_if_default = omit_if_default 

1077 self.forbid_extra_keys = forbid_extra_keys 

1078 self.type_overrides = dict(type_overrides) 

1079 

1080 unstruct_collection_overrides = { 

1081 get_origin(k) or k: v for k, v in unstruct_collection_overrides.items() 

1082 } 

1083 

1084 self._unstruct_collection_overrides = unstruct_collection_overrides 

1085 

1086 # Do a little post-processing magic to make things easier for users. 

1087 co = unstruct_collection_overrides 

1088 

1089 # abc.Set overrides, if defined, apply to abc.MutableSets and sets 

1090 if OriginAbstractSet in co: 

1091 if OriginMutableSet not in co: 

1092 co[OriginMutableSet] = co[OriginAbstractSet] 

1093 if FrozenSetSubscriptable not in co: 

1094 co[FrozenSetSubscriptable] = co[OriginAbstractSet] 

1095 

1096 # abc.MutableSet overrrides, if defined, apply to sets 

1097 if OriginMutableSet in co and set not in co: 

1098 co[set] = co[OriginMutableSet] 

1099 

1100 # abc.Sequence overrides, if defined, can apply to MutableSequences, lists and 

1101 # tuples 

1102 if Sequence in co: 

1103 if MutableSequence not in co: 

1104 co[MutableSequence] = co[Sequence] 

1105 if tuple not in co: 

1106 co[tuple] = co[Sequence] 

1107 

1108 # abc.MutableSequence overrides, if defined, can apply to lists 

1109 if MutableSequence in co: 

1110 if list not in co: 

1111 co[list] = co[MutableSequence] 

1112 if deque not in co: 

1113 co[deque] = co[MutableSequence] 

1114 

1115 # abc.Mapping overrides, if defined, can apply to MutableMappings 

1116 if Mapping in co and MutableMapping not in co: 

1117 co[MutableMapping] = co[Mapping] 

1118 

1119 # abc.MutableMapping overrides, if defined, can apply to dicts 

1120 if MutableMapping in co and dict not in co: 

1121 co[dict] = co[MutableMapping] 

1122 

1123 # builtins.dict overrides, if defined, can apply to counters 

1124 if dict in co and Counter not in co: 

1125 co[Counter] = co[dict] 

1126 

1127 if unstruct_strat is UnstructureStrategy.AS_DICT: 

1128 # Override the attrs handler. 

1129 self.register_unstructure_hook_factory( 

1130 has_with_generic, self.gen_unstructure_attrs_fromdict 

1131 ) 

1132 self.register_structure_hook_factory( 

1133 has_with_generic, self.gen_structure_attrs_fromdict 

1134 ) 

1135 self.register_unstructure_hook_factory( 

1136 is_annotated, self.gen_unstructure_annotated 

1137 ) 

1138 self.register_unstructure_hook_factory( 

1139 is_hetero_tuple, self.gen_unstructure_hetero_tuple 

1140 ) 

1141 self.register_unstructure_hook_factory(is_namedtuple)( 

1142 namedtuple_unstructure_factory 

1143 ) 

1144 self.register_unstructure_hook_factory( 

1145 is_sequence, self.gen_unstructure_iterable 

1146 ) 

1147 self.register_unstructure_hook_factory(is_mapping, self.gen_unstructure_mapping) 

1148 self.register_unstructure_hook_factory( 

1149 is_mutable_set, 

1150 lambda cl: self.gen_unstructure_iterable(cl, unstructure_to=set), 

1151 ) 

1152 self.register_unstructure_hook_factory( 

1153 is_frozenset, 

1154 lambda cl: self.gen_unstructure_iterable(cl, unstructure_to=frozenset), 

1155 ) 

1156 self.register_unstructure_hook_factory( 

1157 is_optional, self.gen_unstructure_optional 

1158 ) 

1159 self.register_unstructure_hook_factory( 

1160 is_typeddict, self.gen_unstructure_typeddict 

1161 ) 

1162 self.register_unstructure_hook_factory( 

1163 lambda t: get_newtype_base(t) is not None, 

1164 lambda t: self.get_unstructure_hook(get_newtype_base(t)), 

1165 ) 

1166 

1167 self.register_structure_hook_factory(is_annotated, self.gen_structure_annotated) 

1168 self.register_structure_hook_factory(is_mapping, self.gen_structure_mapping) 

1169 self.register_structure_hook_factory(is_counter, self.gen_structure_counter) 

1170 self.register_structure_hook_factory( 

1171 is_defaultdict, defaultdict_structure_factory 

1172 ) 

1173 self.register_structure_hook_factory(is_typeddict, self.gen_structure_typeddict) 

1174 self.register_structure_hook_factory( 

1175 lambda t: get_newtype_base(t) is not None, self.get_structure_newtype 

1176 ) 

1177 

1178 # We keep these so we can more correctly copy the hooks. 

1179 self._struct_copy_skip = self._structure_func.get_num_fns() 

1180 self._unstruct_copy_skip = self._unstructure_func.get_num_fns() 

1181 

1182 @overload 

1183 def register_unstructure_hook_factory( 

1184 self, predicate: Predicate 

1185 ) -> Callable[[AnyUnstructureHookFactory], AnyUnstructureHookFactory]: ... 

1186 

1187 @overload 

1188 def register_unstructure_hook_factory( 

1189 self, predicate: Predicate, factory: UnstructureHookFactory 

1190 ) -> UnstructureHookFactory: ... 

1191 

1192 @overload 

1193 def register_unstructure_hook_factory( 

1194 self, predicate: Predicate, factory: ExtendedUnstructureHookFactory[Converter] 

1195 ) -> ExtendedUnstructureHookFactory[Converter]: ... 

1196 

1197 def register_unstructure_hook_factory(self, predicate, factory=None): 

1198 # This dummy wrapper is required due to how `@overload` works. 

1199 return super().register_unstructure_hook_factory(predicate, factory) 

1200 

1201 @overload 

1202 def register_structure_hook_factory( 

1203 self, predicate: Predicate 

1204 ) -> Callable[[AnyStructureHookFactory], AnyStructureHookFactory]: ... 

1205 

1206 @overload 

1207 def register_structure_hook_factory( 

1208 self, predicate: Predicate, factory: StructureHookFactory 

1209 ) -> StructureHookFactory: ... 

1210 

1211 @overload 

1212 def register_structure_hook_factory( 

1213 self, predicate: Predicate, factory: ExtendedStructureHookFactory[Converter] 

1214 ) -> ExtendedStructureHookFactory[Converter]: ... 

1215 

1216 def register_structure_hook_factory(self, predicate, factory=None): 

1217 # This dummy wrapper is required due to how `@overload` works. 

1218 return super().register_structure_hook_factory(predicate, factory) 

1219 

1220 def get_structure_newtype(self, type: type[T]) -> Callable[[Any, Any], T]: 

1221 base = get_newtype_base(type) 

1222 handler = self.get_structure_hook(base) 

1223 return lambda v, _: handler(v, base) 

1224 

1225 def gen_unstructure_annotated(self, type): 

1226 origin = type.__origin__ 

1227 return self.get_unstructure_hook(origin) 

1228 

1229 def gen_structure_annotated(self, type) -> Callable: 

1230 """A hook factory for annotated types.""" 

1231 origin = type.__origin__ 

1232 hook = self.get_structure_hook(origin) 

1233 return lambda v, _: hook(v, origin) 

1234 

1235 def gen_unstructure_typeddict(self, cl: Any) -> Callable[[dict], dict]: 

1236 """Generate a TypedDict unstructure function. 

1237 

1238 Also apply converter-scored modifications. 

1239 """ 

1240 return make_typeddict_dict_unstruct_fn(cl, self) 

1241 

1242 def gen_unstructure_attrs_fromdict( 

1243 self, cl: type[T] 

1244 ) -> Callable[[T], dict[str, Any]]: 

1245 origin = get_origin(cl) 

1246 attribs = fields(origin or cl) 

1247 if attrs_has(cl) and any(isinstance(a.type, str) for a in attribs): 

1248 # PEP 563 annotations - need to be resolved. 

1249 resolve_types(cl) 

1250 attrib_overrides = { 

1251 a.name: self.type_overrides[a.type] 

1252 for a in attribs 

1253 if a.type in self.type_overrides 

1254 } 

1255 

1256 return make_dict_unstructure_fn( 

1257 cl, self, _cattrs_omit_if_default=self.omit_if_default, **attrib_overrides 

1258 ) 

1259 

1260 def gen_unstructure_optional(self, cl: type[T]) -> Callable[[T], Any]: 

1261 """Generate an unstructuring hook for optional types.""" 

1262 union_params = cl.__args__ 

1263 other = union_params[0] if union_params[1] is NoneType else union_params[1] 

1264 

1265 if isinstance(other, TypeVar): 

1266 handler = self.unstructure 

1267 else: 

1268 handler = self.get_unstructure_hook(other) 

1269 

1270 def unstructure_optional(val, _handler=handler): 

1271 return None if val is None else _handler(val) 

1272 

1273 return unstructure_optional 

1274 

1275 def gen_structure_typeddict(self, cl: Any) -> Callable[[dict, Any], dict]: 

1276 """Generate a TypedDict structure function. 

1277 

1278 Also apply converter-scored modifications. 

1279 """ 

1280 return make_typeddict_dict_struct_fn( 

1281 cl, self, _cattrs_detailed_validation=self.detailed_validation 

1282 ) 

1283 

1284 def gen_structure_attrs_fromdict( 

1285 self, cl: type[T] 

1286 ) -> Callable[[Mapping[str, Any], Any], T]: 

1287 attribs = fields(get_origin(cl) or cl if is_generic(cl) else cl) 

1288 if attrs_has(cl) and any(isinstance(a.type, str) for a in attribs): 

1289 # PEP 563 annotations - need to be resolved. 

1290 resolve_types(cl) 

1291 attrib_overrides = { 

1292 a.name: self.type_overrides[a.type] 

1293 for a in attribs 

1294 if a.type in self.type_overrides 

1295 } 

1296 return make_dict_structure_fn( 

1297 cl, 

1298 self, 

1299 _cattrs_forbid_extra_keys=self.forbid_extra_keys, 

1300 _cattrs_prefer_attrib_converters=self._prefer_attrib_converters, 

1301 _cattrs_detailed_validation=self.detailed_validation, 

1302 **attrib_overrides, 

1303 ) 

1304 

1305 def gen_unstructure_iterable( 

1306 self, cl: Any, unstructure_to: Any = None 

1307 ) -> IterableUnstructureFn: 

1308 unstructure_to = self._unstruct_collection_overrides.get( 

1309 get_origin(cl) or cl, unstructure_to or list 

1310 ) 

1311 h = iterable_unstructure_factory(cl, self, unstructure_to=unstructure_to) 

1312 self._unstructure_func.register_cls_list([(cl, h)], direct=True) 

1313 return h 

1314 

1315 def gen_unstructure_hetero_tuple( 

1316 self, cl: Any, unstructure_to: Any = None 

1317 ) -> HeteroTupleUnstructureFn: 

1318 unstructure_to = self._unstruct_collection_overrides.get( 

1319 get_origin(cl) or cl, unstructure_to or tuple 

1320 ) 

1321 h = make_hetero_tuple_unstructure_fn(cl, self, unstructure_to=unstructure_to) 

1322 self._unstructure_func.register_cls_list([(cl, h)], direct=True) 

1323 return h 

1324 

1325 def gen_unstructure_mapping( 

1326 self, 

1327 cl: Any, 

1328 unstructure_to: Any = None, 

1329 key_handler: Callable[[Any, Any | None], Any] | None = None, 

1330 ) -> MappingUnstructureFn: 

1331 unstructure_to = self._unstruct_collection_overrides.get( 

1332 get_origin(cl) or cl, unstructure_to or dict 

1333 ) 

1334 h = mapping_unstructure_factory( 

1335 cl, self, unstructure_to=unstructure_to, key_handler=key_handler 

1336 ) 

1337 self._unstructure_func.register_cls_list([(cl, h)], direct=True) 

1338 return h 

1339 

1340 def gen_structure_counter( 

1341 self, cl: type[CounterT] 

1342 ) -> SimpleStructureHook[Mapping[Any, Any], CounterT]: 

1343 h = mapping_structure_factory( 

1344 cl, 

1345 self, 

1346 structure_to=Counter, 

1347 val_type=int, 

1348 detailed_validation=self.detailed_validation, 

1349 ) 

1350 self._structure_func.register_cls_list([(cl, h)], direct=True) 

1351 return h 

1352 

1353 def gen_structure_mapping( 

1354 self, cl: Any 

1355 ) -> SimpleStructureHook[Mapping[Any, Any], Any]: 

1356 structure_to = get_origin(cl) or cl 

1357 if structure_to in ( 

1358 MutableMapping, 

1359 AbcMutableMapping, 

1360 Mapping, 

1361 AbcMapping, 

1362 ): # These default to dicts 

1363 structure_to = dict 

1364 h = mapping_structure_factory( 

1365 cl, self, structure_to, detailed_validation=self.detailed_validation 

1366 ) 

1367 self._structure_func.register_cls_list([(cl, h)], direct=True) 

1368 return h 

1369 

1370 def copy( 

1371 self, 

1372 dict_factory: Callable[[], Any] | None = None, 

1373 unstruct_strat: UnstructureStrategy | None = None, 

1374 omit_if_default: bool | None = None, 

1375 forbid_extra_keys: bool | None = None, 

1376 type_overrides: Mapping[type, AttributeOverride] | None = None, 

1377 unstruct_collection_overrides: Mapping[type, UnstructureHook] | None = None, 

1378 prefer_attrib_converters: bool | None = None, 

1379 detailed_validation: bool | None = None, 

1380 ) -> Self: 

1381 """Create a copy of the converter, keeping all existing custom hooks. 

1382 

1383 :param detailed_validation: Whether to use a slightly slower mode for detailed 

1384 validation errors. 

1385 """ 

1386 res = self.__class__( 

1387 dict_factory if dict_factory is not None else self._dict_factory, 

1388 ( 

1389 unstruct_strat 

1390 if unstruct_strat is not None 

1391 else ( 

1392 UnstructureStrategy.AS_DICT 

1393 if self._unstructure_attrs == self.unstructure_attrs_asdict 

1394 else UnstructureStrategy.AS_TUPLE 

1395 ) 

1396 ), 

1397 omit_if_default if omit_if_default is not None else self.omit_if_default, 

1398 ( 

1399 forbid_extra_keys 

1400 if forbid_extra_keys is not None 

1401 else self.forbid_extra_keys 

1402 ), 

1403 type_overrides if type_overrides is not None else self.type_overrides, 

1404 ( 

1405 unstruct_collection_overrides 

1406 if unstruct_collection_overrides is not None 

1407 else self._unstruct_collection_overrides 

1408 ), 

1409 ( 

1410 prefer_attrib_converters 

1411 if prefer_attrib_converters is not None 

1412 else self._prefer_attrib_converters 

1413 ), 

1414 ( 

1415 detailed_validation 

1416 if detailed_validation is not None 

1417 else self.detailed_validation 

1418 ), 

1419 ) 

1420 

1421 self._unstructure_func.copy_to( 

1422 res._unstructure_func, skip=self._unstruct_copy_skip 

1423 ) 

1424 self._structure_func.copy_to(res._structure_func, skip=self._struct_copy_skip) 

1425 

1426 return res 

1427 

1428 

1429GenConverter: TypeAlias = Converter