3
3
from dataclasses import Field
4
4
from enum import Enum
5
5
from functools import lru_cache
6
- from typing import Any , Callable , Dict , Optional , Tuple , Type , TypeVar , Union
6
+ from typing import (
7
+ Any ,
8
+ Callable ,
9
+ Dict ,
10
+ Iterable ,
11
+ List ,
12
+ NoReturn ,
13
+ Optional ,
14
+ Tuple ,
15
+ Type ,
16
+ TypeVar ,
17
+ Union ,
18
+ )
7
19
8
20
from attr import Attribute
9
21
from attr import has as attrs_has
19
31
OriginAbstractSet ,
20
32
OriginMutableSet ,
21
33
Sequence ,
34
+ Set ,
22
35
fields ,
23
36
get_newtype_base ,
24
37
get_origin ,
43
56
from .dispatch import MultiStrategyDispatch
44
57
from .gen import (
45
58
AttributeOverride ,
59
+ DictStructureFn ,
60
+ HeteroTupleUnstructureFn ,
61
+ IterableUnstructureFn ,
62
+ MappingStructureFn ,
63
+ MappingUnstructureFn ,
46
64
make_dict_structure_fn ,
47
65
make_dict_unstructure_fn ,
48
66
make_hetero_tuple_unstructure_fn ,
@@ -63,26 +81,26 @@ class UnstructureStrategy(Enum):
63
81
AS_TUPLE = "astuple"
64
82
65
83
66
- def _subclass (typ ) :
84
+ def _subclass (typ : Type ) -> Callable [[ Type ], bool ] :
67
85
"""a shortcut"""
68
86
return lambda cls : issubclass (cls , typ )
69
87
70
88
71
- def is_attrs_union (typ ) :
89
+ def is_attrs_union (typ : Type ) -> bool :
72
90
return is_union_type (typ ) and all (has (get_origin (e ) or e ) for e in typ .__args__ )
73
91
74
92
75
- def is_attrs_union_or_none (typ ) :
93
+ def is_attrs_union_or_none (typ : Type ) -> bool :
76
94
return is_union_type (typ ) and all (
77
95
e is NoneType or has (get_origin (e ) or e ) for e in typ .__args__
78
96
)
79
97
80
98
81
- def is_optional (typ ) :
99
+ def is_optional (typ : Type ) -> bool :
82
100
return is_union_type (typ ) and NoneType in typ .__args__ and len (typ .__args__ ) == 2
83
101
84
102
85
- def is_literal_containing_enums (typ ) :
103
+ def is_literal_containing_enums (typ : Type ) -> bool :
86
104
return is_literal (typ ) and any (isinstance (val , Enum ) for val in typ .__args__ )
87
105
88
106
@@ -212,8 +230,8 @@ def register_unstructure_hook(self, cls: Any, func: Callable[[Any], Any]) -> Non
212
230
self ._unstructure_func .register_cls_list ([(cls , func )])
213
231
214
232
def register_unstructure_hook_func (
215
- self , check_func : Callable [[Any ], bool ], func : Callable [[T ], Any ]
216
- ):
233
+ self , check_func : Callable [[Any ], bool ], func : Callable [[Any ], Any ]
234
+ ) -> None :
217
235
"""Register a class-to-primitive converter function for a class, using
218
236
a function to check if it's a match.
219
237
"""
@@ -235,7 +253,9 @@ def register_unstructure_hook_factory(
235
253
"""
236
254
self ._unstructure_func .register_func_list ([(predicate , factory , True )])
237
255
238
- def register_structure_hook (self , cl : Any , func : Callable [[Any , Type [T ]], T ]):
256
+ def register_structure_hook (
257
+ self , cl : Any , func : Callable [[Any , Type [T ]], T ]
258
+ ) -> None :
239
259
"""Register a primitive-to-class converter function for a type.
240
260
241
261
The converter function should take two arguments:
@@ -255,7 +275,7 @@ def register_structure_hook(self, cl: Any, func: Callable[[Any, Type[T]], T]):
255
275
256
276
def register_structure_hook_func (
257
277
self , check_func : Callable [[Type [T ]], bool ], func : Callable [[Any , Type [T ]], T ]
258
- ):
278
+ ) -> None :
259
279
"""Register a class-to-primitive converter function for a class, using
260
280
a function to check if it's a match.
261
281
"""
@@ -283,7 +303,7 @@ def structure(self, obj: Any, cl: Type[T]) -> T:
283
303
return self ._structure_func .dispatch (cl )(obj , cl )
284
304
285
305
# Classes to Python primitives.
286
- def unstructure_attrs_asdict (self , obj ) -> Dict [str , Any ]:
306
+ def unstructure_attrs_asdict (self , obj : Any ) -> Dict [str , Any ]:
287
307
"""Our version of `attrs.asdict`, so we can call back to us."""
288
308
attrs = fields (obj .__class__ )
289
309
dispatch = self ._unstructure_func .dispatch
@@ -294,7 +314,7 @@ def unstructure_attrs_asdict(self, obj) -> Dict[str, Any]:
294
314
rv [name ] = dispatch (a .type or v .__class__ )(v )
295
315
return rv
296
316
297
- def unstructure_attrs_astuple (self , obj ) -> Tuple [Any , ...]:
317
+ def unstructure_attrs_astuple (self , obj : Any ) -> Tuple [Any , ...]:
298
318
"""Our version of `attrs.astuple`, so we can call back to us."""
299
319
attrs = fields (obj .__class__ )
300
320
dispatch = self ._unstructure_func .dispatch
@@ -305,22 +325,22 @@ def unstructure_attrs_astuple(self, obj) -> Tuple[Any, ...]:
305
325
res .append (dispatch (a .type or v .__class__ )(v ))
306
326
return tuple (res )
307
327
308
- def _unstructure_enum (self , obj ) :
328
+ def _unstructure_enum (self , obj : Enum ) -> Any :
309
329
"""Convert an enum to its value."""
310
330
return obj .value
311
331
312
332
@staticmethod
313
- def _unstructure_identity (obj ) :
333
+ def _unstructure_identity (obj : T ) -> T :
314
334
"""Just pass it through."""
315
335
return obj
316
336
317
- def _unstructure_seq (self , seq ) :
337
+ def _unstructure_seq (self , seq : Sequence [ T ]) -> Sequence [ T ] :
318
338
"""Convert a sequence to primitive equivalents."""
319
339
# We can reuse the sequence class, so tuples stay tuples.
320
340
dispatch = self ._unstructure_func .dispatch
321
341
return seq .__class__ (dispatch (e .__class__ )(e ) for e in seq )
322
342
323
- def _unstructure_mapping (self , mapping ) :
343
+ def _unstructure_mapping (self , mapping : Mapping [ T , V ]) -> Mapping [ T , V ] :
324
344
"""Convert a mapping of attr classes to primitive equivalents."""
325
345
326
346
# We can reuse the mapping class, so dicts stay dicts and OrderedDicts
@@ -331,7 +351,10 @@ def _unstructure_mapping(self, mapping):
331
351
for k , v in mapping .items ()
332
352
)
333
353
334
- def _unstructure_union (self , obj ):
354
+ # note: Use UnionType when 3.11 is released as
355
+ # the behaviour of @final is changed. This would
356
+ # affect how we can support UnionType in ._compat.py
357
+ def _unstructure_union (self , obj : Any ) -> Any :
335
358
"""
336
359
Unstructure an object as a union.
337
360
@@ -342,19 +365,21 @@ def _unstructure_union(self, obj):
342
365
# Python primitives to classes.
343
366
344
367
@staticmethod
345
- def _structure_error (_ , cl ) :
368
+ def _structure_error (_ , cl : Type ) -> NoReturn :
346
369
"""At the bottom of the condition stack, we explode if we can't handle it."""
347
370
msg = "Unsupported type: {0!r}. Register a structure hook for " "it." .format (cl )
348
371
raise StructureHandlerNotFoundError (msg , type_ = cl )
349
372
350
- def _gen_structure_generic (self , cl ) :
373
+ def _gen_structure_generic (self , cl : Type [ T ]) -> DictStructureFn [ T ] :
351
374
"""Create and return a hook for structuring generics."""
352
375
fn = make_dict_structure_fn (
353
376
cl , self , _cattrs_prefer_attrib_converters = self ._prefer_attrib_converters
354
377
)
355
378
return fn
356
379
357
- def _gen_attrs_union_structure (self , cl ):
380
+ def _gen_attrs_union_structure (
381
+ self , cl : Any
382
+ ) -> Callable [[Any , Type [T ]], Optional [Type [T ]]]:
358
383
"""Generate a structuring function for a union of attrs classes (and maybe None)."""
359
384
dis_fn = self ._get_dis_func (cl )
360
385
has_none = NoneType in cl .__args__
@@ -374,7 +399,7 @@ def structure_attrs_union(obj, _):
374
399
return structure_attrs_union
375
400
376
401
@staticmethod
377
- def _structure_call (obj , cl ) :
402
+ def _structure_call (obj : Any , cl : Type [ T ]) -> Any :
378
403
"""Just call ``cl`` with the given ``obj``.
379
404
380
405
This is just an optimization on the ``_structure_default`` case, when
@@ -411,7 +436,7 @@ def structure_attrs_fromtuple(self, obj: Tuple[Any, ...], cl: Type[T]) -> T:
411
436
converted = self ._structure_attribute (a , value )
412
437
conv_obj .append (converted )
413
438
414
- return cl (* conv_obj ) # type: ignore
439
+ return cl (* conv_obj )
415
440
416
441
def _structure_attribute (self , a : Union [Attribute , Field ], value : Any ) -> Any :
417
442
"""Handle an individual attrs attribute."""
@@ -440,7 +465,7 @@ def structure_attrs_fromdict(self, obj: Mapping[str, Any], cl: Type[T]) -> T:
440
465
# For public use.
441
466
442
467
conv_obj = {} # Start with a fresh dict, to ignore extra keys.
443
- for a in fields (cl ): # type: ignore
468
+ for a in fields (cl ):
444
469
name = a .name
445
470
446
471
try :
@@ -453,9 +478,9 @@ def structure_attrs_fromdict(self, obj: Mapping[str, Any], cl: Type[T]) -> T:
453
478
454
479
conv_obj [name ] = self ._structure_attribute (a , val )
455
480
456
- return cl (** conv_obj ) # type: ignore
481
+ return cl (** conv_obj )
457
482
458
- def _structure_list (self , obj , cl ) :
483
+ def _structure_list (self , obj : Iterable [ T ] , cl : Any ) -> List [ T ] :
459
484
"""Convert an iterable to a potentially generic list."""
460
485
if is_bare (cl ) or cl .__args__ [0 ] is Any :
461
486
res = [e for e in obj ]
@@ -482,7 +507,9 @@ def _structure_list(self, obj, cl):
482
507
res = [handler (e , elem_type ) for e in obj ]
483
508
return res
484
509
485
- def _structure_set (self , obj , cl , structure_to = set ):
510
+ def _structure_set (
511
+ self , obj : Iterable [T ], cl : Any , structure_to : type = set
512
+ ) -> Set [T ]:
486
513
"""Convert an iterable into a potentially generic set."""
487
514
if is_bare (cl ) or cl .__args__ [0 ] is Any :
488
515
return structure_to (obj )
@@ -507,11 +534,13 @@ def _structure_set(self, obj, cl, structure_to=set):
507
534
else :
508
535
return structure_to ([handler (e , elem_type ) for e in obj ])
509
536
510
- def _structure_frozenset (self , obj , cl ):
537
+ def _structure_frozenset (
538
+ self , obj : Iterable [T ], cl : Any
539
+ ) -> FrozenSetSubscriptable [T ]:
511
540
"""Convert an iterable into a potentially generic frozenset."""
512
- return self ._structure_set (obj , cl , structure_to = frozenset )
541
+ return self ._structure_set (obj , cl , structure_to = frozenset ) # type: ignore (incompatible type between frozenset and set)
513
542
514
- def _structure_dict (self , obj , cl ) :
543
+ def _structure_dict (self , obj : Mapping [ T , V ], cl : Any ) -> Dict [ T , V ] :
515
544
"""Convert a mapping into a potentially generic dict."""
516
545
if is_bare (cl ) or cl .__args__ == (Any , Any ):
517
546
return dict (obj )
@@ -543,7 +572,7 @@ def _structure_union(self, obj, union):
543
572
handler = self ._union_struct_registry [union ]
544
573
return handler (obj , union )
545
574
546
- def _structure_tuple (self , obj , tup : Type [T ]) -> T :
575
+ def _structure_tuple (self , obj : Any , tup : Type [T ]) -> T :
547
576
"""Deal with structuring into a tuple."""
548
577
if tup in (Tuple , tuple ):
549
578
tup_params = None
@@ -853,23 +882,32 @@ def gen_structure_attrs_fromdict(
853
882
# only direct dispatch so that subclasses get separately generated
854
883
return h
855
884
856
- def gen_unstructure_iterable (self , cl : Any , unstructure_to = None ):
885
+ def gen_unstructure_iterable (
886
+ self , cl : Any , unstructure_to : Any = None
887
+ ) -> IterableUnstructureFn :
857
888
unstructure_to = self ._unstruct_collection_overrides .get (
858
889
get_origin (cl ) or cl , unstructure_to or list
859
890
)
860
891
h = make_iterable_unstructure_fn (cl , self , unstructure_to = unstructure_to )
861
892
self ._unstructure_func .register_cls_list ([(cl , h )], direct = True )
862
893
return h
863
894
864
- def gen_unstructure_hetero_tuple (self , cl : Any , unstructure_to = None ):
895
+ def gen_unstructure_hetero_tuple (
896
+ self , cl : Any , unstructure_to : Any = None
897
+ ) -> HeteroTupleUnstructureFn :
865
898
unstructure_to = self ._unstruct_collection_overrides .get (
866
899
get_origin (cl ) or cl , unstructure_to or list
867
900
)
868
901
h = make_hetero_tuple_unstructure_fn (cl , self , unstructure_to = unstructure_to )
869
902
self ._unstructure_func .register_cls_list ([(cl , h )], direct = True )
870
903
return h
871
904
872
- def gen_unstructure_mapping (self , cl : Any , unstructure_to = None , key_handler = None ):
905
+ def gen_unstructure_mapping (
906
+ self ,
907
+ cl : Any ,
908
+ unstructure_to : Any = None ,
909
+ key_handler : Optional [Callable [[Any , Optional [Any ]], Any ]] = None ,
910
+ ) -> MappingUnstructureFn :
873
911
unstructure_to = self ._unstruct_collection_overrides .get (
874
912
get_origin (cl ) or cl , unstructure_to or dict
875
913
)
@@ -879,7 +917,7 @@ def gen_unstructure_mapping(self, cl: Any, unstructure_to=None, key_handler=None
879
917
self ._unstructure_func .register_cls_list ([(cl , h )], direct = True )
880
918
return h
881
919
882
- def gen_structure_counter (self , cl : Any ):
920
+ def gen_structure_counter (self , cl : Any ) -> MappingStructureFn [ T ] :
883
921
h = make_mapping_structure_fn (
884
922
cl ,
885
923
self ,
@@ -890,7 +928,7 @@ def gen_structure_counter(self, cl: Any):
890
928
self ._structure_func .register_cls_list ([(cl , h )], direct = True )
891
929
return h
892
930
893
- def gen_structure_mapping (self , cl : Any ):
931
+ def gen_structure_mapping (self , cl : Any ) -> MappingStructureFn [ T ] :
894
932
h = make_mapping_structure_fn (
895
933
cl , self , detailed_validation = self .detailed_validation
896
934
)
0 commit comments