3838import re
3939import sys
4040import codecs
41- from typing import Optional , Union , AnyStr , IO , Mapping
41+ from typing import (
42+ Dict ,
43+ Any ,
44+ cast ,
45+ Optional ,
46+ Union ,
47+ AnyStr ,
48+ IO ,
49+ Mapping ,
50+ Callable ,
51+ TypeVar ,
52+ MutableMapping ,
53+ Type ,
54+ List ,
55+ Mapping ,
56+ )
4257
4358try :
4459 from urllib import quote # type: ignore
4863
4964import isodate # type: ignore
5065
51- from typing import Dict , Any , cast
52-
5366from azure .core .exceptions import DeserializationError , SerializationError , raise_with_traceback
67+ from azure .core .serialization import NULL as AzureCoreNull
5468
5569_BOM = codecs .BOM_UTF8 .decode (encoding = "utf-8" )
5670
71+ ModelType = TypeVar ("ModelType" , bound = "Model" )
72+ JSON = MutableMapping [str , Any ]
73+
5774
5875class RawDeserializer :
5976
@@ -277,8 +294,8 @@ class Model(object):
277294 _attribute_map : Dict [str , Dict [str , Any ]] = {}
278295 _validation : Dict [str , Dict [str , Any ]] = {}
279296
280- def __init__ (self , ** kwargs ) :
281- self .additional_properties = {}
297+ def __init__ (self , ** kwargs : Any ) -> None :
298+ self .additional_properties : Dict [ str , Any ] = {}
282299 for k in kwargs :
283300 if k not in self ._attribute_map :
284301 _LOGGER .warning ("%s is not a known attribute of class %s and will be ignored" , k , self .__class__ )
@@ -287,25 +304,25 @@ def __init__(self, **kwargs):
287304 else :
288305 setattr (self , k , kwargs [k ])
289306
290- def __eq__ (self , other ) :
307+ def __eq__ (self , other : Any ) -> bool :
291308 """Compare objects by comparing all attributes."""
292309 if isinstance (other , self .__class__ ):
293310 return self .__dict__ == other .__dict__
294311 return False
295312
296- def __ne__ (self , other ) :
313+ def __ne__ (self , other : Any ) -> bool :
297314 """Compare objects by comparing all attributes."""
298315 return not self .__eq__ (other )
299316
300- def __str__ (self ):
317+ def __str__ (self ) -> str :
301318 return str (self .__dict__ )
302319
303320 @classmethod
304- def enable_additional_properties_sending (cls ):
321+ def enable_additional_properties_sending (cls ) -> None :
305322 cls ._attribute_map ["additional_properties" ] = {"key" : "" , "type" : "{object}" }
306323
307324 @classmethod
308- def is_xml_model (cls ):
325+ def is_xml_model (cls ) -> bool :
309326 try :
310327 cls ._xml_map # type: ignore
311328 except AttributeError :
@@ -322,7 +339,7 @@ def _create_xml_node(cls):
322339
323340 return _create_xml_node (xml_map .get ("name" , cls .__name__ ), xml_map .get ("prefix" , None ), xml_map .get ("ns" , None ))
324341
325- def serialize (self , keep_readonly = False , ** kwargs ) :
342+ def serialize (self , keep_readonly : bool = False , ** kwargs : Any ) -> JSON :
326343 """Return the JSON that would be sent to azure from this model.
327344
328345 This is an alias to `as_dict(full_restapi_key_transformer, keep_readonly=False)`.
@@ -336,8 +353,13 @@ def serialize(self, keep_readonly=False, **kwargs):
336353 serializer = Serializer (self ._infer_class_models ())
337354 return serializer ._serialize (self , keep_readonly = keep_readonly , ** kwargs )
338355
339- def as_dict (self , keep_readonly = True , key_transformer = attribute_transformer , ** kwargs ):
340- """Return a dict that can be JSONify using json.dump.
356+ def as_dict (
357+ self ,
358+ keep_readonly : bool = True ,
359+ key_transformer : Callable [[str , Dict [str , Any ], Any ], Any ] = attribute_transformer ,
360+ ** kwargs : Any
361+ ) -> JSON :
362+ """Return a dict that can be serialized using json.dump.
341363
342364 Advanced usage might optionally use a callback as parameter:
343365
@@ -384,7 +406,7 @@ def _infer_class_models(cls):
384406 return client_models
385407
386408 @classmethod
387- def deserialize (cls , data , content_type = None ):
409+ def deserialize (cls : Type [ ModelType ] , data : Any , content_type : Optional [ str ] = None ) -> ModelType :
388410 """Parse a str using the RestAPI syntax and return a model.
389411
390412 :param str data: A str using RestAPI structure. JSON by default.
@@ -396,7 +418,12 @@ def deserialize(cls, data, content_type=None):
396418 return deserializer (cls .__name__ , data , content_type = content_type )
397419
398420 @classmethod
399- def from_dict (cls , data , key_extractors = None , content_type = None ):
421+ def from_dict (
422+ cls : Type [ModelType ],
423+ data : Any ,
424+ key_extractors : Optional [Callable [[str , Dict [str , Any ], Any ], Any ]] = None ,
425+ content_type : Optional [str ] = None ,
426+ ) -> ModelType :
400427 """Parse a dict using given key extractor return a model.
401428
402429 By default consider key
@@ -409,8 +436,8 @@ def from_dict(cls, data, key_extractors=None, content_type=None):
409436 :raises: DeserializationError if something went wrong
410437 """
411438 deserializer = Deserializer (cls ._infer_class_models ())
412- deserializer .key_extractors = (
413- [
439+ deserializer .key_extractors = ( # type: ignore
440+ [ # type: ignore
414441 attribute_key_case_insensitive_extractor ,
415442 rest_key_case_insensitive_extractor ,
416443 last_rest_key_case_insensitive_extractor ,
@@ -518,7 +545,7 @@ class Serializer(object):
518545 "multiple" : lambda x , y : x % y != 0 ,
519546 }
520547
521- def __init__ (self , classes = None ):
548+ def __init__ (self , classes : Optional [ Mapping [ str , Type [ ModelType ]]] = None ):
522549 self .serialize_type = {
523550 "iso-8601" : Serializer .serialize_iso ,
524551 "rfc-1123" : Serializer .serialize_rfc ,
@@ -534,7 +561,7 @@ def __init__(self, classes=None):
534561 "[]" : self .serialize_iter ,
535562 "{}" : self .serialize_dict ,
536563 }
537- self .dependencies = dict (classes ) if classes else {}
564+ self .dependencies : Dict [ str , Type [ ModelType ]] = dict (classes ) if classes else {}
538565 self .key_transformer = full_restapi_key_transformer
539566 self .client_side_validation = True
540567
@@ -626,8 +653,7 @@ def _serialize(self, target_obj, data_type=None, **kwargs):
626653 serialized .append (local_node ) # type: ignore
627654 else : # JSON
628655 for k in reversed (keys ): # type: ignore
629- unflattened = {k : new_attr }
630- new_attr = unflattened
656+ new_attr = {k : new_attr }
631657
632658 _new_attr = new_attr
633659 _serialized = serialized
@@ -656,8 +682,8 @@ def body(self, data, data_type, **kwargs):
656682 """
657683
658684 # Just in case this is a dict
659- internal_data_type = data_type .strip ("[]{}" )
660- internal_data_type = self .dependencies .get (internal_data_type , None )
685+ internal_data_type_str = data_type .strip ("[]{}" )
686+ internal_data_type = self .dependencies .get (internal_data_type_str , None )
661687 try :
662688 is_xml_model_serialization = kwargs ["is_xml" ]
663689 except KeyError :
@@ -777,6 +803,8 @@ def serialize_data(self, data, data_type, **kwargs):
777803 raise ValueError ("No value for given attribute" )
778804
779805 try :
806+ if data is AzureCoreNull :
807+ return None
780808 if data_type in self .basic_types .values ():
781809 return self .serialize_basic (data , data_type , ** kwargs )
782810
@@ -1161,7 +1189,8 @@ def rest_key_extractor(attr, attr_desc, data):
11611189 working_data = data
11621190
11631191 while "." in key :
1164- dict_keys = _FLATTEN .split (key )
1192+ # Need the cast, as for some reasons "split" is typed as list[str | Any]
1193+ dict_keys = cast (List [str ], _FLATTEN .split (key ))
11651194 if len (dict_keys ) == 1 :
11661195 key = _decode_attribute_map_key (dict_keys [0 ])
11671196 break
@@ -1332,7 +1361,7 @@ class Deserializer(object):
13321361
13331362 valid_date = re .compile (r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?" )
13341363
1335- def __init__ (self , classes = None ):
1364+ def __init__ (self , classes : Optional [ Mapping [ str , Type [ ModelType ]]] = None ):
13361365 self .deserialize_type = {
13371366 "iso-8601" : Deserializer .deserialize_iso ,
13381367 "rfc-1123" : Deserializer .deserialize_rfc ,
@@ -1352,7 +1381,7 @@ def __init__(self, classes=None):
13521381 "duration" : (isodate .Duration , datetime .timedelta ),
13531382 "iso-8601" : (datetime .datetime ),
13541383 }
1355- self .dependencies = dict (classes ) if classes else {}
1384+ self .dependencies : Dict [ str , Type [ ModelType ]] = dict (classes ) if classes else {}
13561385 self .key_extractors = [rest_key_extractor , xml_key_extractor ]
13571386 # Additional properties only works if the "rest_key_extractor" is used to
13581387 # extract the keys. Making it to work whatever the key extractor is too much
@@ -1471,7 +1500,7 @@ def _classify_target(self, target, data):
14711500 Once classification has been determined, initialize object.
14721501
14731502 :param str target: The target object type to deserialize to.
1474- :param str/dict data: The response data to deseralize .
1503+ :param str/dict data: The response data to deserialize .
14751504 """
14761505 if target is None :
14771506 return None , None
@@ -1486,7 +1515,7 @@ def _classify_target(self, target, data):
14861515 target = target ._classify (data , self .dependencies )
14871516 except AttributeError :
14881517 pass # Target is not a Model, no classify
1489- return target , target .__class__ .__name__
1518+ return target , target .__class__ .__name__ # type: ignore
14901519
14911520 def failsafe_deserialize (self , target_obj , data , content_type = None ):
14921521 """Ignores any errors encountered in deserialization,
@@ -1496,7 +1525,7 @@ def failsafe_deserialize(self, target_obj, data, content_type=None):
14961525 a deserialization error.
14971526
14981527 :param str target_obj: The target object type to deserialize to.
1499- :param str/dict data: The response data to deseralize .
1528+ :param str/dict data: The response data to deserialize .
15001529 :param str content_type: Swagger "produces" if available.
15011530 """
15021531 try :
0 commit comments