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

536 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_sequence, 

47 is_mutable_set, 

48 is_optional, 

49 is_protocol, 

50 is_subclass, 

51 is_tuple, 

52 is_typeddict, 

53 is_union_type, 

54 signature, 

55) 

56from .cols import ( 

57 defaultdict_structure_factory, 

58 homogenous_tuple_structure_factory, 

59 is_abstract_set, 

60 is_defaultdict, 

61 is_namedtuple, 

62 is_sequence, 

63 iterable_unstructure_factory, 

64 list_structure_factory, 

65 mapping_structure_factory, 

66 mapping_unstructure_factory, 

67 namedtuple_structure_factory, 

68 namedtuple_unstructure_factory, 

69) 

70from .disambiguators import create_default_dis_func, is_supported_union 

71from .dispatch import ( 

72 HookFactory, 

73 MultiStrategyDispatch, 

74 StructuredValue, 

75 StructureHook, 

76 TargetType, 

77 UnstructuredValue, 

78 UnstructureHook, 

79) 

80from .enums import enum_structure_factory, enum_unstructure_factory 

81from .errors import ( 

82 IterableValidationError, 

83 IterableValidationNote, 

84 StructureHandlerNotFoundError, 

85) 

86from .fns import Predicate, identity, raise_error 

87from .gen import ( 

88 AttributeOverride, 

89 HeteroTupleUnstructureFn, 

90 IterableUnstructureFn, 

91 MappingUnstructureFn, 

92 make_dict_structure_fn, 

93 make_dict_unstructure_fn, 

94 make_hetero_tuple_unstructure_fn, 

95) 

96from .gen.typeddicts import make_dict_structure_fn as make_typeddict_dict_struct_fn 

97from .gen.typeddicts import make_dict_unstructure_fn as make_typeddict_dict_unstruct_fn 

98from .literals import is_literal_containing_enums 

99from .typealiases import ( 

100 get_type_alias_base, 

101 is_type_alias, 

102 type_alias_structure_factory, 

103) 

104from .types import SimpleStructureHook 

105 

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

107 

108T = TypeVar("T") 

109V = TypeVar("V") 

110 

111UnstructureHookFactory = TypeVar( 

112 "UnstructureHookFactory", bound=HookFactory[UnstructureHook] 

113) 

114 

115# The Extended factory also takes a converter. 

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

117 

118# This typevar for the BaseConverter. 

119AnyUnstructureHookFactoryBase = TypeVar( 

120 "AnyUnstructureHookFactoryBase", 

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

122) 

123 

124# This typevar for the Converter. 

125AnyUnstructureHookFactory = TypeVar( 

126 "AnyUnstructureHookFactory", 

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

128) 

129 

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

131 

132# The Extended factory also takes a converter. 

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

134 

135# This typevar for the BaseConverter. 

136AnyStructureHookFactoryBase = TypeVar( 

137 "AnyStructureHookFactoryBase", 

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

139) 

140 

141# This typevar for the Converter. 

142AnyStructureHookFactory = TypeVar( 

143 "AnyStructureHookFactory", 

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

145) 

146 

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

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

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

150 

151 

152class UnstructureStrategy(Enum): 

153 """`attrs` classes unstructuring strategies.""" 

154 

155 AS_DICT = "asdict" 

156 AS_TUPLE = "astuple" 

157 

158 

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

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

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

162 # annotations. 

163 sig = inspect_signature(factory) 

164 return ( 

165 len(sig.parameters) >= 2 

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

167 ) 

168 

169 

170class BaseConverter: 

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

172 

173 __slots__ = ( 

174 "_dict_factory", 

175 "_prefer_attrib_converters", 

176 "_struct_copy_skip", 

177 "_structure_attrs", 

178 "_structure_func", 

179 "_union_struct_registry", 

180 "_unstruct_copy_skip", 

181 "_unstructure_attrs", 

182 "_unstructure_func", 

183 "detailed_validation", 

184 ) 

185 

186 def __init__( 

187 self, 

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

189 unstruct_strat: UnstructureStrategy = UnstructureStrategy.AS_DICT, 

190 prefer_attrib_converters: bool = False, 

191 detailed_validation: bool = True, 

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

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

194 None, t 

195 ), 

196 ) -> None: 

197 """ 

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

199 validation errors. 

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

201 registered unstructuring hooks match. 

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

203 registered structuring hooks match. 

204 

205 .. versionadded:: 23.2.0 *unstructure_fallback_factory* 

206 .. versionadded:: 23.2.0 *structure_fallback_factory* 

207 .. versionchanged:: 24.2.0 

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

209 more eagerly, surfacing problems earlier. 

210 """ 

211 unstruct_strat = UnstructureStrategy(unstruct_strat) 

212 self._prefer_attrib_converters = prefer_attrib_converters 

213 

214 self.detailed_validation = detailed_validation 

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

216 

217 # Create a per-instance cache. 

218 if unstruct_strat is UnstructureStrategy.AS_DICT: 

219 self._unstructure_attrs = self.unstructure_attrs_asdict 

220 self._structure_attrs = self.structure_attrs_fromdict 

221 else: 

222 self._unstructure_attrs = self.unstructure_attrs_astuple 

223 self._structure_attrs = self.structure_attrs_fromtuple 

224 

225 self._unstructure_func = MultiStrategyDispatch( 

226 unstructure_fallback_factory, self 

227 ) 

228 self._unstructure_func.register_cls_list( 

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

230 ) 

231 self._unstructure_func.register_func_list( 

232 [ 

233 ( 

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

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

236 ), 

237 ( 

238 is_protocol, 

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

240 ), 

241 ( 

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

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

244 True, 

245 ), 

246 ( 

247 is_type_alias, 

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

249 True, 

250 ), 

251 (is_mapping, self._unstructure_mapping), 

252 (is_sequence, self._unstructure_seq), 

253 (is_mutable_set, self._unstructure_seq), 

254 (is_frozenset, self._unstructure_seq), 

255 (is_literal_containing_enums, self.unstructure), 

256 (lambda t: is_subclass(t, Enum), enum_unstructure_factory, "extended"), 

257 (has, self._unstructure_attrs), 

258 (is_union_type, self._unstructure_union), 

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

260 ] 

261 ) 

262 

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

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

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

266 self._structure_func = MultiStrategyDispatch(structure_fallback_factory, self) 

267 self._structure_func.register_func_list( 

268 [ 

269 ( 

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

271 lambda v, _: v, 

272 ), 

273 (is_generic_attrs, self._gen_structure_generic, True), 

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

275 (is_type_alias, type_alias_structure_factory, "extended"), 

276 ( 

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

278 self._structure_final_factory, 

279 True, 

280 ), 

281 (is_literal, self._structure_simple_literal), 

282 (is_literal_containing_enums, self._structure_enum_literal), 

283 (is_sequence, homogenous_tuple_structure_factory, "extended"), 

284 (is_mutable_sequence, list_structure_factory, "extended"), 

285 (is_deque, self._structure_deque), 

286 (is_mutable_set, self._structure_set), 

287 (is_abstract_set, self._structure_frozenset), 

288 (is_frozenset, self._structure_frozenset), 

289 (is_tuple, self._structure_tuple), 

290 (is_namedtuple, namedtuple_structure_factory, "extended"), 

291 (is_mapping, self._structure_dict), 

292 *( 

293 [(is_supported_union, self._gen_attrs_union_structure, True)] 

294 if unstruct_strat is UnstructureStrategy.AS_DICT 

295 else [] 

296 ), 

297 (is_optional, self._structure_optional), 

298 ( 

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

300 self._union_struct_registry.__getitem__, 

301 True, 

302 ), 

303 (lambda t: is_subclass(t, Enum), enum_structure_factory, "extended"), 

304 (has, self._structure_attrs), 

305 ] 

306 ) 

307 # Strings are sequences. 

308 self._structure_func.register_cls_list( 

309 [ 

310 (str, self._structure_call), 

311 (bytes, self._structure_call), 

312 (int, self._structure_call), 

313 (float, self._structure_call), 

314 (Path, self._structure_call), 

315 ] 

316 ) 

317 

318 self._dict_factory = dict_factory 

319 

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

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

322 

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

324 return self._unstructure_func.dispatch( 

325 obj.__class__ if unstructure_as is None else unstructure_as 

326 )(obj) 

327 

328 @property 

329 def unstruct_strat(self) -> UnstructureStrategy: 

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

331 return ( 

332 UnstructureStrategy.AS_DICT 

333 if self._unstructure_attrs == self.unstructure_attrs_asdict 

334 else UnstructureStrategy.AS_TUPLE 

335 ) 

336 

337 @overload 

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

339 

340 @overload 

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

342 

343 def register_unstructure_hook( 

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

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

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

347 

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

349 its Python equivalent. 

350 

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

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

353 type to register the hook for. 

354 

355 .. versionchanged:: 24.1.0 

356 This method may now be used as a decorator. 

357 .. versionchanged:: 25.1.0 

358 Modern type aliases are now supported. 

359 """ 

360 if func is None: 

361 # Autodetecting decorator. 

362 func = cls 

363 sig = signature(func) 

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

365 self.register_unstructure_hook(cls, func) 

366 

367 return func 

368 

369 if attrs_has(cls): 

370 resolve_types(cls) 

371 if is_union_type(cls): 

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

373 elif is_type_alias(cls): 

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

375 elif get_newtype_base(cls) is not None: 

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

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

378 else: 

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

380 return None 

381 

382 def register_unstructure_hook_func( 

383 self, check_func: Predicate, func: UnstructureHook 

384 ) -> None: 

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

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

387 """ 

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

389 

390 @overload 

391 def register_unstructure_hook_factory( 

392 self, predicate: Predicate 

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

394 

395 @overload 

396 def register_unstructure_hook_factory( 

397 self, predicate: Predicate, factory: UnstructureHookFactory 

398 ) -> UnstructureHookFactory: ... 

399 

400 @overload 

401 def register_unstructure_hook_factory( 

402 self, 

403 predicate: Predicate, 

404 factory: ExtendedUnstructureHookFactory[BaseConverter], 

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

406 

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

408 """ 

409 Register a hook factory for a given predicate. 

410 

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

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

413 parameter. 

414 

415 May also be used as a decorator. 

416 

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

418 can produce a hook for that type. 

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

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

421 

422 .. versionchanged:: 24.1.0 

423 This method may now be used as a decorator. 

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

425 """ 

426 if factory is None: 

427 

428 def decorator(factory): 

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

430 if _is_extended_factory(factory): 

431 self._unstructure_func.register_func_list( 

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

433 ) 

434 else: 

435 self._unstructure_func.register_func_list( 

436 [(predicate, factory, True)] 

437 ) 

438 

439 return decorator 

440 

441 self._unstructure_func.register_func_list( 

442 [ 

443 ( 

444 predicate, 

445 factory, 

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

447 ) 

448 ] 

449 ) 

450 return factory 

451 

452 def get_unstructure_hook( 

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

454 ) -> UnstructureHook: 

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

456 

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

458 and re-registered. 

459 

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

461 will be used to produce one. 

462 

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

464 

465 .. versionadded:: 24.1.0 

466 """ 

467 return ( 

468 self._unstructure_func.dispatch(type) 

469 if cache_result 

470 else self._unstructure_func.dispatch_without_caching(type) 

471 ) 

472 

473 @overload 

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

475 

476 @overload 

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

478 

479 def register_structure_hook( 

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

481 ) -> None: 

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

483 

484 The converter function should take two arguments: 

485 * a Python object to be converted, 

486 * the type to convert to 

487 

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

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

490 

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

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

493 as the type for the hook. 

494 

495 .. versionchanged:: 24.1.0 

496 This method may now be used as a decorator. 

497 .. versionchanged:: 25.1.0 

498 Modern type aliases are now supported. 

499 """ 

500 if func is None: 

501 # The autodetecting decorator. 

502 func = cl 

503 sig = signature(func) 

504 self.register_structure_hook(sig.return_annotation, func) 

505 return func 

506 

507 if attrs_has(cl): 

508 resolve_types(cl) 

509 if is_union_type(cl): 

510 self._union_struct_registry[cl] = func 

511 self._structure_func.clear_cache() 

512 elif is_type_alias(cl): 

513 # Type aliases are special-cased. 

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

515 elif get_newtype_base(cl) is not None: 

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

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

518 else: 

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

520 return None 

521 

522 def register_structure_hook_func( 

523 self, check_func: Predicate, func: StructureHook 

524 ) -> None: 

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

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

527 """ 

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

529 

530 @overload 

531 def register_structure_hook_factory( 

532 self, predicate: Predicate 

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

534 

535 @overload 

536 def register_structure_hook_factory( 

537 self, predicate: Predicate, factory: StructureHookFactory 

538 ) -> StructureHookFactory: ... 

539 

540 @overload 

541 def register_structure_hook_factory( 

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

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

544 

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

546 """ 

547 Register a hook factory for a given predicate. 

548 

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

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

551 parameter. 

552 

553 May also be used as a decorator. 

554 

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

556 can produce a hook for that type. 

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

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

559 

560 .. versionchanged:: 24.1.0 

561 This method may now be used as a decorator. 

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

563 """ 

564 if factory is None: 

565 # Decorator use. 

566 def decorator(factory): 

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

568 if _is_extended_factory(factory): 

569 self._structure_func.register_func_list( 

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

571 ) 

572 else: 

573 self._structure_func.register_func_list( 

574 [(predicate, factory, True)] 

575 ) 

576 

577 return decorator 

578 self._structure_func.register_func_list( 

579 [ 

580 ( 

581 predicate, 

582 factory, 

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

584 ) 

585 ] 

586 ) 

587 return factory 

588 

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

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

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

592 

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

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

595 

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

597 and re-registered. 

598 

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

600 will be used to produce one. 

601 

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

603 

604 .. versionadded:: 24.1.0 

605 """ 

606 return ( 

607 self._structure_func.dispatch(type) 

608 if cache_result 

609 else self._structure_func.dispatch_without_caching(type) 

610 ) 

611 

612 # Classes to Python primitives. 

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

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

615 attrs = fields(obj.__class__) 

616 dispatch = self._unstructure_func.dispatch 

617 rv = self._dict_factory() 

618 for a in attrs: 

619 name = a.name 

620 v = getattr(obj, name) 

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

622 return rv 

623 

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

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

626 attrs = fields(obj.__class__) 

627 dispatch = self._unstructure_func.dispatch 

628 res = [] 

629 for a in attrs: 

630 name = a.name 

631 v = getattr(obj, name) 

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

633 return tuple(res) 

634 

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

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

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

638 dispatch = self._unstructure_func.dispatch 

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

640 

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

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

643 

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

645 # stay OrderedDicts. 

646 dispatch = self._unstructure_func.dispatch 

647 return mapping.__class__( 

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

649 for k, v in mapping.items() 

650 ) 

651 

652 # note: Use UnionType when 3.11 is released as 

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

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

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

656 """ 

657 Unstructure an object as a union. 

658 

659 By default, just unstructures the instance. 

660 """ 

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

662 

663 # Python primitives to classes. 

664 

665 def _gen_structure_generic( 

666 self, cl: type[T] 

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

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

669 return make_dict_structure_fn( 

670 cl, self, _cattrs_prefer_attrib_converters=self._prefer_attrib_converters 

671 ) 

672 

673 def _gen_attrs_union_structure( 

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

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

676 """ 

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

678 

679 :param use_literals: Whether to consider literal fields. 

680 """ 

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

682 has_none = NoneType in cl.__args__ 

683 

684 if has_none: 

685 

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

687 if obj is None: 

688 return None 

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

690 

691 else: 

692 

693 def structure_attrs_union(obj, _): 

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

695 

696 return structure_attrs_union 

697 

698 @staticmethod 

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

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

701 

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

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

704 etc. 

705 """ 

706 return cl(obj) 

707 

708 @staticmethod 

709 def _structure_simple_literal(val, type): 

710 if val not in type.__args__: 

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

712 return val 

713 

714 @staticmethod 

715 def _structure_enum_literal(val, type): 

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

717 try: 

718 return vals[val] 

719 except KeyError: 

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

721 

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

723 base = get_newtype_base(type) 

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

725 

726 def _structure_final_factory(self, type): 

727 base = get_final_base(type) 

728 res = self.get_structure_hook(base) 

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

730 

731 # Attrs classes. 

732 

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

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

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

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

737 # We detect the type by the metadata. 

738 converted = self._structure_attribute(a, value) 

739 conv_obj.append(converted) 

740 

741 return cl(*conv_obj) 

742 

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

744 """Handle an individual attrs attribute.""" 

745 type_ = a.type 

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

747 if self._prefer_attrib_converters and attrib_converter: 

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

749 # prefer_attrib_converters is set to give these priority over registered 

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

751 # into the converter 

752 return value 

753 if type_ is None: 

754 # No type metadata. 

755 return value 

756 

757 try: 

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

759 except StructureHandlerNotFoundError: 

760 if attrib_converter: 

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

762 return value 

763 raise 

764 

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

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

767 # For public use. 

768 

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

770 for a in fields(cl): 

771 try: 

772 val = obj[a.name] 

773 except KeyError: 

774 continue 

775 

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

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

778 

779 return cl(**conv_obj) 

780 

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

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

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

784 res = deque(obj) 

785 else: 

786 elem_type = cl.__args__[0] 

787 handler = self._structure_func.dispatch(elem_type) 

788 if self.detailed_validation: 

789 errors = [] 

790 res = deque() 

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

792 for e in obj: 

793 try: 

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

795 except Exception as e: 

796 msg = IterableValidationNote( 

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

798 ) 

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

800 errors.append(e) 

801 finally: 

802 ix += 1 

803 if errors: 

804 raise IterableValidationError( 

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

806 ) 

807 else: 

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

809 return res 

810 

811 def _structure_set( 

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

813 ) -> Set[T]: 

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

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

816 return structure_to(obj) 

817 elem_type = cl.__args__[0] 

818 handler = self._structure_func.dispatch(elem_type) 

819 if self.detailed_validation: 

820 errors = [] 

821 res = set() 

822 ix = 0 

823 for e in obj: 

824 try: 

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

826 except Exception as exc: 

827 msg = IterableValidationNote( 

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

829 ix, 

830 elem_type, 

831 ) 

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

833 errors.append(exc) 

834 finally: 

835 ix += 1 

836 if errors: 

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

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

839 if structure_to is set: 

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

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

842 

843 def _structure_frozenset( 

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

845 ) -> FrozenSetSubscriptable[T]: 

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

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

848 

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

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

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

852 return dict(obj) 

853 key_type, val_type = cl.__args__ 

854 

855 if self.detailed_validation: 

856 key_handler = self._structure_func.dispatch(key_type) 

857 val_handler = self._structure_func.dispatch(val_type) 

858 errors = [] 

859 res = {} 

860 

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

862 try: 

863 value = val_handler(v, val_type) 

864 except Exception as exc: 

865 msg = IterableValidationNote( 

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

867 ) 

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

869 errors.append(exc) 

870 continue 

871 

872 try: 

873 key = key_handler(k, key_type) 

874 res[key] = value 

875 except Exception as exc: 

876 msg = IterableValidationNote( 

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

878 ) 

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

880 errors.append(exc) 

881 

882 if errors: 

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

884 return res 

885 

886 if key_type in ANIES: 

887 val_conv = self._structure_func.dispatch(val_type) 

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

889 if val_type in ANIES: 

890 key_conv = self._structure_func.dispatch(key_type) 

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

892 key_conv = self._structure_func.dispatch(key_type) 

893 val_conv = self._structure_func.dispatch(val_type) 

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

895 

896 def _structure_optional(self, obj, union): 

897 if obj is None: 

898 return None 

899 union_params = union.__args__ 

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

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

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

903 

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

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

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

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

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

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

910 return tuple(obj) 

911 if has_ellipsis: 

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

913 tup_type = tup_params[0] 

914 conv = self._structure_func.dispatch(tup_type) 

915 if self.detailed_validation: 

916 errors = [] 

917 res = [] 

918 ix = 0 

919 for e in obj: 

920 try: 

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

922 except Exception as exc: 

923 msg = IterableValidationNote( 

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

925 ) 

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

927 errors.append(exc) 

928 finally: 

929 ix += 1 

930 if errors: 

931 raise IterableValidationError( 

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

933 ) 

934 return tuple(res) 

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

936 

937 # We're dealing with a heterogenous tuple. 

938 exp_len = len(tup_params) 

939 if self.detailed_validation: 

940 errors = [] 

941 res = [] 

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

943 try: 

944 conv = self._structure_func.dispatch(t) 

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

946 except Exception as exc: 

947 msg = IterableValidationNote( 

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

949 ) 

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

951 errors.append(exc) 

952 if len(obj) != exp_len: 

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

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

955 msg = f"Structuring {tup}" 

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

957 errors.append(exc) 

958 if errors: 

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

960 return tuple(res) 

961 

962 if len(obj) != exp_len: 

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

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

965 return tuple( 

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

967 ) 

968 

969 def _get_dis_func( 

970 self, 

971 union: Any, 

972 use_literals: bool = True, 

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

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

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

976 union_types = union.__args__ 

977 if NoneType in union_types: 

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

979 # logic. 

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

981 

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

983 raise StructureHandlerNotFoundError( 

984 "Only unions of attrs classes and dataclasses supported " 

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

986 type_=union, 

987 ) 

988 

989 return create_default_dis_func( 

990 self, 

991 *union_types, 

992 use_literals=use_literals, 

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

994 ) 

995 

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

997 return self.copy() 

998 

999 def copy( 

1000 self, 

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

1002 unstruct_strat: UnstructureStrategy | None = None, 

1003 prefer_attrib_converters: bool | None = None, 

1004 detailed_validation: bool | None = None, 

1005 ) -> Self: 

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

1007 

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

1009 validation errors. 

1010 """ 

1011 res = self.__class__( 

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

1013 ( 

1014 unstruct_strat 

1015 if unstruct_strat is not None 

1016 else ( 

1017 UnstructureStrategy.AS_DICT 

1018 if self._unstructure_attrs == self.unstructure_attrs_asdict 

1019 else UnstructureStrategy.AS_TUPLE 

1020 ) 

1021 ), 

1022 ( 

1023 prefer_attrib_converters 

1024 if prefer_attrib_converters is not None 

1025 else self._prefer_attrib_converters 

1026 ), 

1027 ( 

1028 detailed_validation 

1029 if detailed_validation is not None 

1030 else self.detailed_validation 

1031 ), 

1032 ) 

1033 

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

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

1036 

1037 return res 

1038 

1039 

1040class Converter(BaseConverter): 

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

1042 

1043 __slots__ = ( 

1044 "_unstruct_collection_overrides", 

1045 "forbid_extra_keys", 

1046 "omit_if_default", 

1047 "type_overrides", 

1048 "use_alias", 

1049 ) 

1050 

1051 def __init__( 

1052 self, 

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

1054 unstruct_strat: UnstructureStrategy = UnstructureStrategy.AS_DICT, 

1055 omit_if_default: bool = False, 

1056 forbid_extra_keys: bool = False, 

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

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

1059 prefer_attrib_converters: bool = False, 

1060 detailed_validation: bool = True, 

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

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

1063 None, t 

1064 ), 

1065 use_alias: bool = False, 

1066 ): 

1067 """ 

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

1069 validation errors. 

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

1071 registered unstructuring hooks match. 

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

1073 registered structuring hooks match. 

1074 :param use_alias: Whether to use the field alias instead of the field name as 

1075 the un/structured dictionary key by default. 

1076 

1077 .. versionadded:: 23.2.0 *unstructure_fallback_factory* 

1078 .. versionadded:: 23.2.0 *structure_fallback_factory* 

1079 .. versionchanged:: 24.2.0 

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

1081 more eagerly, surfacing problems earlier. 

1082 .. versionadded:: 25.2.0 *use_alias* 

1083 """ 

1084 super().__init__( 

1085 dict_factory=dict_factory, 

1086 unstruct_strat=unstruct_strat, 

1087 prefer_attrib_converters=prefer_attrib_converters, 

1088 detailed_validation=detailed_validation, 

1089 unstructure_fallback_factory=unstructure_fallback_factory, 

1090 structure_fallback_factory=structure_fallback_factory, 

1091 ) 

1092 self.omit_if_default = omit_if_default 

1093 self.forbid_extra_keys = forbid_extra_keys 

1094 self.type_overrides = dict(type_overrides) 

1095 self.use_alias = use_alias 

1096 

1097 unstruct_collection_overrides = { 

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

1099 } 

1100 

1101 self._unstruct_collection_overrides = unstruct_collection_overrides 

1102 

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

1104 co = unstruct_collection_overrides 

1105 

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

1107 if OriginAbstractSet in co: 

1108 if OriginMutableSet not in co: 

1109 co[OriginMutableSet] = co[OriginAbstractSet] 

1110 if FrozenSetSubscriptable not in co: 

1111 co[FrozenSetSubscriptable] = co[OriginAbstractSet] 

1112 

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

1114 if OriginMutableSet in co and set not in co: 

1115 co[set] = co[OriginMutableSet] 

1116 

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

1118 # tuples 

1119 if Sequence in co: 

1120 if MutableSequence not in co: 

1121 co[MutableSequence] = co[Sequence] 

1122 if tuple not in co: 

1123 co[tuple] = co[Sequence] 

1124 

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

1126 if MutableSequence in co: 

1127 if list not in co: 

1128 co[list] = co[MutableSequence] 

1129 if deque not in co: 

1130 co[deque] = co[MutableSequence] 

1131 

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

1133 if Mapping in co and MutableMapping not in co: 

1134 co[MutableMapping] = co[Mapping] 

1135 

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

1137 if MutableMapping in co and dict not in co: 

1138 co[dict] = co[MutableMapping] 

1139 

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

1141 if dict in co and Counter not in co: 

1142 co[Counter] = co[dict] 

1143 

1144 if unstruct_strat is UnstructureStrategy.AS_DICT: 

1145 # Override the attrs handler. 

1146 self.register_unstructure_hook_factory( 

1147 has_with_generic, self.gen_unstructure_attrs_fromdict 

1148 ) 

1149 self.register_structure_hook_factory( 

1150 has_with_generic, self.gen_structure_attrs_fromdict 

1151 ) 

1152 self.register_unstructure_hook_factory( 

1153 is_annotated, self.gen_unstructure_annotated 

1154 ) 

1155 self.register_unstructure_hook_factory( 

1156 is_hetero_tuple, self.gen_unstructure_hetero_tuple 

1157 ) 

1158 self.register_unstructure_hook_factory(is_namedtuple)( 

1159 namedtuple_unstructure_factory 

1160 ) 

1161 self.register_unstructure_hook_factory( 

1162 is_sequence, self.gen_unstructure_iterable 

1163 ) 

1164 self.register_unstructure_hook_factory(is_mapping, self.gen_unstructure_mapping) 

1165 self.register_unstructure_hook_factory( 

1166 is_mutable_set, 

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

1168 ) 

1169 self.register_unstructure_hook_factory( 

1170 is_frozenset, 

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

1172 ) 

1173 self.register_unstructure_hook_factory( 

1174 is_optional, self.gen_unstructure_optional 

1175 ) 

1176 self.register_unstructure_hook_factory( 

1177 is_typeddict, self.gen_unstructure_typeddict 

1178 ) 

1179 self.register_unstructure_hook_factory( 

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

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

1182 ) 

1183 

1184 self.register_structure_hook_factory(is_annotated, self.gen_structure_annotated) 

1185 self.register_structure_hook_factory(is_mapping, self.gen_structure_mapping) 

1186 self.register_structure_hook_factory(is_counter, self.gen_structure_counter) 

1187 self.register_structure_hook_factory( 

1188 is_defaultdict, defaultdict_structure_factory 

1189 ) 

1190 self.register_structure_hook_factory(is_typeddict, self.gen_structure_typeddict) 

1191 self.register_structure_hook_factory( 

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

1193 ) 

1194 

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

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

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

1198 

1199 @overload 

1200 def register_unstructure_hook_factory( 

1201 self, predicate: Predicate 

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

1203 

1204 @overload 

1205 def register_unstructure_hook_factory( 

1206 self, predicate: Predicate, factory: UnstructureHookFactory 

1207 ) -> UnstructureHookFactory: ... 

1208 

1209 @overload 

1210 def register_unstructure_hook_factory( 

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

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

1213 

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

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

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

1217 

1218 @overload 

1219 def register_structure_hook_factory( 

1220 self, predicate: Predicate 

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

1222 

1223 @overload 

1224 def register_structure_hook_factory( 

1225 self, predicate: Predicate, factory: StructureHookFactory 

1226 ) -> StructureHookFactory: ... 

1227 

1228 @overload 

1229 def register_structure_hook_factory( 

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

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

1232 

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

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

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

1236 

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

1238 base = get_newtype_base(type) 

1239 handler = self.get_structure_hook(base) 

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

1241 

1242 def gen_unstructure_annotated(self, type): 

1243 origin = type.__origin__ 

1244 return self.get_unstructure_hook(origin) 

1245 

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

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

1248 origin = type.__origin__ 

1249 hook = self.get_structure_hook(origin) 

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

1251 

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

1253 """Generate a TypedDict unstructure function. 

1254 

1255 Also apply converter-scored modifications. 

1256 """ 

1257 return make_typeddict_dict_unstruct_fn(cl, self) 

1258 

1259 def gen_unstructure_attrs_fromdict( 

1260 self, cl: type[T] 

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

1262 origin = get_origin(cl) 

1263 attribs = fields(origin or cl) 

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

1265 # PEP 563 annotations - need to be resolved. 

1266 resolve_types(origin or cl) 

1267 attrib_overrides = { 

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

1269 for a in attribs 

1270 if a.type in self.type_overrides 

1271 } 

1272 

1273 return make_dict_unstructure_fn( 

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

1275 ) 

1276 

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

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

1279 union_params = cl.__args__ 

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

1281 

1282 if isinstance(other, TypeVar): 

1283 handler = self.unstructure 

1284 else: 

1285 handler = self.get_unstructure_hook(other) 

1286 

1287 def unstructure_optional(val, _handler=handler): 

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

1289 

1290 return unstructure_optional 

1291 

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

1293 """Generate a TypedDict structure function. 

1294 

1295 Also apply converter-scored modifications. 

1296 """ 

1297 return make_typeddict_dict_struct_fn( 

1298 cl, self, _cattrs_detailed_validation=self.detailed_validation 

1299 ) 

1300 

1301 def gen_structure_attrs_fromdict( 

1302 self, cl: type[T] 

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

1304 origin = get_origin(cl) 

1305 attribs = fields(origin or cl if is_generic(cl) else cl) 

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

1307 # PEP 563 annotations - need to be resolved. 

1308 resolve_types(origin or cl) 

1309 attrib_overrides = { 

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

1311 for a in attribs 

1312 if a.type in self.type_overrides 

1313 } 

1314 return make_dict_structure_fn( 

1315 cl, 

1316 self, 

1317 _cattrs_forbid_extra_keys=self.forbid_extra_keys, 

1318 _cattrs_prefer_attrib_converters=self._prefer_attrib_converters, 

1319 _cattrs_detailed_validation=self.detailed_validation, 

1320 _cattrs_use_alias=self.use_alias, 

1321 **attrib_overrides, 

1322 ) 

1323 

1324 def gen_unstructure_iterable( 

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

1326 ) -> IterableUnstructureFn: 

1327 unstructure_to = self._unstruct_collection_overrides.get( 

1328 get_origin(cl) or cl, unstructure_to or list 

1329 ) 

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

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

1332 return h 

1333 

1334 def gen_unstructure_hetero_tuple( 

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

1336 ) -> HeteroTupleUnstructureFn: 

1337 unstructure_to = self._unstruct_collection_overrides.get( 

1338 get_origin(cl) or cl, unstructure_to or tuple 

1339 ) 

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

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

1342 return h 

1343 

1344 def gen_unstructure_mapping( 

1345 self, 

1346 cl: Any, 

1347 unstructure_to: Any = None, 

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

1349 ) -> MappingUnstructureFn: 

1350 unstructure_to = self._unstruct_collection_overrides.get( 

1351 get_origin(cl) or cl, unstructure_to or dict 

1352 ) 

1353 h = mapping_unstructure_factory( 

1354 cl, self, unstructure_to=unstructure_to, key_handler=key_handler 

1355 ) 

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

1357 return h 

1358 

1359 def gen_structure_counter( 

1360 self, cl: type[CounterT] 

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

1362 h = mapping_structure_factory( 

1363 cl, 

1364 self, 

1365 structure_to=Counter, 

1366 val_type=int, 

1367 detailed_validation=self.detailed_validation, 

1368 ) 

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

1370 return h 

1371 

1372 def gen_structure_mapping( 

1373 self, cl: Any 

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

1375 structure_to = get_origin(cl) or cl 

1376 if structure_to in ( 

1377 MutableMapping, 

1378 AbcMutableMapping, 

1379 Mapping, 

1380 AbcMapping, 

1381 ): # These default to dicts 

1382 structure_to = dict 

1383 h = mapping_structure_factory( 

1384 cl, self, structure_to, detailed_validation=self.detailed_validation 

1385 ) 

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

1387 return h 

1388 

1389 def copy( 

1390 self, 

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

1392 unstruct_strat: UnstructureStrategy | None = None, 

1393 omit_if_default: bool | None = None, 

1394 forbid_extra_keys: bool | None = None, 

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

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

1397 prefer_attrib_converters: bool | None = None, 

1398 detailed_validation: bool | None = None, 

1399 use_alias: bool | None = None, 

1400 ) -> Self: 

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

1402 

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

1404 validation errors. 

1405 """ 

1406 res = self.__class__( 

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

1408 ( 

1409 unstruct_strat 

1410 if unstruct_strat is not None 

1411 else ( 

1412 UnstructureStrategy.AS_DICT 

1413 if self._unstructure_attrs == self.unstructure_attrs_asdict 

1414 else UnstructureStrategy.AS_TUPLE 

1415 ) 

1416 ), 

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

1418 ( 

1419 forbid_extra_keys 

1420 if forbid_extra_keys is not None 

1421 else self.forbid_extra_keys 

1422 ), 

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

1424 ( 

1425 unstruct_collection_overrides 

1426 if unstruct_collection_overrides is not None 

1427 else self._unstruct_collection_overrides 

1428 ), 

1429 ( 

1430 prefer_attrib_converters 

1431 if prefer_attrib_converters is not None 

1432 else self._prefer_attrib_converters 

1433 ), 

1434 ( 

1435 detailed_validation 

1436 if detailed_validation is not None 

1437 else self.detailed_validation 

1438 ), 

1439 use_alias=(use_alias if use_alias is not None else self.use_alias), 

1440 ) 

1441 

1442 self._unstructure_func.copy_to( 

1443 res._unstructure_func, skip=self._unstruct_copy_skip 

1444 ) 

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

1446 

1447 return res 

1448 

1449 

1450GenConverter: TypeAlias = Converter