@autorest/python 6.2.6 → 6.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/autorest/black/__init__.py +3 -2
- package/autorest/codegen/__init__.py +1 -1
- package/autorest/codegen/models/base.py +9 -1
- package/autorest/codegen/models/client.py +4 -2
- package/autorest/codegen/models/operation.py +6 -6
- package/autorest/codegen/models/operation_group.py +0 -6
- package/autorest/codegen/models/primitive_types.py +50 -0
- package/autorest/codegen/serializers/__init__.py +17 -8
- package/autorest/codegen/serializers/builder_serializer.py +61 -24
- package/autorest/codegen/serializers/client_serializer.py +4 -3
- package/autorest/codegen/serializers/metadata_serializer.py +7 -1
- package/autorest/codegen/serializers/model_serializer.py +8 -12
- package/autorest/codegen/serializers/operation_groups_serializer.py +1 -0
- package/autorest/codegen/serializers/parameter_serializer.py +3 -3
- package/autorest/codegen/serializers/patch_serializer.py +2 -4
- package/autorest/codegen/serializers/sample_serializer.py +23 -14
- package/autorest/codegen/serializers/utils.py +6 -0
- package/autorest/codegen/templates/client.py.jinja2 +3 -12
- package/autorest/codegen/templates/config.py.jinja2 +2 -5
- package/autorest/codegen/templates/keywords.jinja2 +2 -2
- package/autorest/codegen/templates/metadata.json.jinja2 +2 -2
- package/autorest/codegen/templates/model_base.py.jinja2 +2 -4
- package/autorest/codegen/templates/model_dpg.py.jinja2 +1 -1
- package/autorest/codegen/templates/request_builder.py.jinja2 +1 -1
- package/autorest/codegen/templates/serialization.py.jinja2 +286 -325
- package/autorest/jsonrpc/stdstream.py +1 -1
- package/autorest/m2r/__init__.py +2 -2
- package/autorest/multiapi/models/imports.py +13 -5
- package/autorest/multiapi/serializers/import_serializer.py +1 -1
- package/autorest/multiapi/templates/multiapi_config.py.jinja2 +2 -8
- package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +1 -1
- package/autorest/postprocess/__init__.py +5 -4
- package/package.json +1 -1
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
# --------------------------------------------------------------------------
|
|
26
26
|
|
|
27
27
|
# pylint: skip-file
|
|
28
|
+
# pyright: reportUnnecessaryTypeIgnoreComment=false
|
|
28
29
|
|
|
29
30
|
from base64 import b64decode, b64encode
|
|
30
31
|
import calendar
|
|
@@ -37,34 +38,33 @@ import logging
|
|
|
37
38
|
import re
|
|
38
39
|
import sys
|
|
39
40
|
import codecs
|
|
41
|
+
from typing import Optional, Union, AnyStr, IO, Mapping
|
|
42
|
+
|
|
40
43
|
try:
|
|
41
44
|
from urllib import quote # type: ignore
|
|
42
45
|
except ImportError:
|
|
43
|
-
from urllib.parse import quote
|
|
46
|
+
from urllib.parse import quote
|
|
44
47
|
import xml.etree.ElementTree as ET
|
|
45
48
|
|
|
46
|
-
import isodate
|
|
49
|
+
import isodate # type: ignore
|
|
47
50
|
|
|
48
|
-
from typing import Dict, Any, cast
|
|
51
|
+
from typing import Dict, Any, cast
|
|
49
52
|
|
|
50
53
|
from azure.core.exceptions import DeserializationError, SerializationError, raise_with_traceback
|
|
51
54
|
|
|
52
|
-
_BOM = codecs.BOM_UTF8.decode(encoding=
|
|
55
|
+
_BOM = codecs.BOM_UTF8.decode(encoding="utf-8")
|
|
53
56
|
|
|
54
|
-
if TYPE_CHECKING:
|
|
55
|
-
from typing import Optional, Union, AnyStr, IO, Mapping
|
|
56
57
|
|
|
57
58
|
class RawDeserializer:
|
|
58
59
|
|
|
59
60
|
# Accept "text" because we're open minded people...
|
|
60
|
-
JSON_REGEXP = re.compile(r
|
|
61
|
+
JSON_REGEXP = re.compile(r"^(application|text)/([a-z+.]+\+)?json$")
|
|
61
62
|
|
|
62
63
|
# Name used in context
|
|
63
64
|
CONTEXT_NAME = "deserialized_data"
|
|
64
65
|
|
|
65
66
|
@classmethod
|
|
66
|
-
def deserialize_from_text(cls, data, content_type=None):
|
|
67
|
-
# type: (Optional[Union[AnyStr, IO]], Optional[str]) -> Any
|
|
67
|
+
def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: Optional[str] = None) -> Any:
|
|
68
68
|
"""Decode data according to content-type.
|
|
69
69
|
|
|
70
70
|
Accept a stream of data as well, but will be load at once in memory for now.
|
|
@@ -75,12 +75,12 @@ class RawDeserializer:
|
|
|
75
75
|
:type data: str or bytes or IO
|
|
76
76
|
:param str content_type: The content type.
|
|
77
77
|
"""
|
|
78
|
-
if hasattr(data,
|
|
78
|
+
if hasattr(data, "read"):
|
|
79
79
|
# Assume a stream
|
|
80
80
|
data = cast(IO, data).read()
|
|
81
81
|
|
|
82
82
|
if isinstance(data, bytes):
|
|
83
|
-
data_as_str = data.decode(encoding=
|
|
83
|
+
data_as_str = data.decode(encoding="utf-8-sig")
|
|
84
84
|
else:
|
|
85
85
|
# Explain to mypy the correct type.
|
|
86
86
|
data_as_str = cast(str, data)
|
|
@@ -116,7 +116,8 @@ class RawDeserializer:
|
|
|
116
116
|
try:
|
|
117
117
|
return True, json.loads(data)
|
|
118
118
|
except ValueError:
|
|
119
|
-
return False, None
|
|
119
|
+
return False, None # Don't care about this one
|
|
120
|
+
|
|
120
121
|
success, json_result = _json_attemp(data)
|
|
121
122
|
if success:
|
|
122
123
|
return json_result
|
|
@@ -129,8 +130,7 @@ class RawDeserializer:
|
|
|
129
130
|
raise DeserializationError("Cannot deserialize content-type: {}".format(content_type))
|
|
130
131
|
|
|
131
132
|
@classmethod
|
|
132
|
-
def deserialize_from_http_generics(cls, body_bytes, headers):
|
|
133
|
-
# type: (Optional[Union[AnyStr, IO]], Mapping) -> Any
|
|
133
|
+
def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], headers: Mapping) -> Any:
|
|
134
134
|
"""Deserialize from HTTP response.
|
|
135
135
|
|
|
136
136
|
Use bytes and headers to NOT use any requests/aiohttp or whatever
|
|
@@ -139,8 +139,8 @@ class RawDeserializer:
|
|
|
139
139
|
"""
|
|
140
140
|
# Try to use content-type from headers if available
|
|
141
141
|
content_type = None
|
|
142
|
-
if
|
|
143
|
-
content_type = headers[
|
|
142
|
+
if "content-type" in headers:
|
|
143
|
+
content_type = headers["content-type"].split(";")[0].strip().lower()
|
|
144
144
|
# Ouch, this server did not declare what it sent...
|
|
145
145
|
# Let's guess it's JSON...
|
|
146
146
|
# Also, since Autorest was considering that an empty body was a valid JSON,
|
|
@@ -152,20 +152,22 @@ class RawDeserializer:
|
|
|
152
152
|
return cls.deserialize_from_text(body_bytes, content_type)
|
|
153
153
|
return None
|
|
154
154
|
|
|
155
|
+
|
|
155
156
|
try:
|
|
156
157
|
basestring # type: ignore
|
|
157
158
|
unicode_str = unicode # type: ignore
|
|
158
159
|
except NameError:
|
|
159
|
-
basestring = str
|
|
160
|
-
unicode_str = str
|
|
160
|
+
basestring = str
|
|
161
|
+
unicode_str = str
|
|
161
162
|
|
|
162
163
|
_LOGGER = logging.getLogger(__name__)
|
|
163
164
|
|
|
164
165
|
try:
|
|
165
|
-
_long_type = long
|
|
166
|
+
_long_type = long # type: ignore
|
|
166
167
|
except NameError:
|
|
167
168
|
_long_type = int
|
|
168
169
|
|
|
170
|
+
|
|
169
171
|
class UTC(datetime.tzinfo):
|
|
170
172
|
"""Time Zone info for handling UTC"""
|
|
171
173
|
|
|
@@ -181,9 +183,11 @@ class UTC(datetime.tzinfo):
|
|
|
181
183
|
"""No daylight saving for UTC."""
|
|
182
184
|
return datetime.timedelta(hours=1)
|
|
183
185
|
|
|
186
|
+
|
|
184
187
|
try:
|
|
185
|
-
from datetime import timezone as _FixedOffset
|
|
188
|
+
from datetime import timezone as _FixedOffset # type: ignore
|
|
186
189
|
except ImportError: # Python 2.7
|
|
190
|
+
|
|
187
191
|
class _FixedOffset(datetime.tzinfo): # type: ignore
|
|
188
192
|
"""Fixed offset in minutes east from UTC.
|
|
189
193
|
Copy/pasted from Python doc
|
|
@@ -197,7 +201,7 @@ except ImportError: # Python 2.7
|
|
|
197
201
|
return self.__offset
|
|
198
202
|
|
|
199
203
|
def tzname(self, dt):
|
|
200
|
-
return str(self.__offset.total_seconds()/3600)
|
|
204
|
+
return str(self.__offset.total_seconds() / 3600)
|
|
201
205
|
|
|
202
206
|
def __repr__(self):
|
|
203
207
|
return "<FixedOffset {}>".format(self.tzname(None))
|
|
@@ -208,14 +212,17 @@ except ImportError: # Python 2.7
|
|
|
208
212
|
def __getinitargs__(self):
|
|
209
213
|
return (self.__offset,)
|
|
210
214
|
|
|
215
|
+
|
|
211
216
|
try:
|
|
212
217
|
from datetime import timezone
|
|
213
|
-
|
|
218
|
+
|
|
219
|
+
TZ_UTC = timezone.utc
|
|
214
220
|
except ImportError:
|
|
215
221
|
TZ_UTC = UTC() # type: ignore
|
|
216
222
|
|
|
217
223
|
_FLATTEN = re.compile(r"(?<!\\)\.")
|
|
218
224
|
|
|
225
|
+
|
|
219
226
|
def attribute_transformer(key, attr_desc, value):
|
|
220
227
|
"""A key transformer that returns the Python attribute.
|
|
221
228
|
|
|
@@ -226,6 +233,7 @@ def attribute_transformer(key, attr_desc, value):
|
|
|
226
233
|
"""
|
|
227
234
|
return (key, value)
|
|
228
235
|
|
|
236
|
+
|
|
229
237
|
def full_restapi_key_transformer(key, attr_desc, value):
|
|
230
238
|
"""A key transformer that returns the full RestAPI key path.
|
|
231
239
|
|
|
@@ -234,9 +242,10 @@ def full_restapi_key_transformer(key, attr_desc, value):
|
|
|
234
242
|
:param object value: The value
|
|
235
243
|
:returns: A list of keys using RestAPI syntax.
|
|
236
244
|
"""
|
|
237
|
-
keys = _FLATTEN.split(attr_desc[
|
|
245
|
+
keys = _FLATTEN.split(attr_desc["key"])
|
|
238
246
|
return ([_decode_attribute_map_key(k) for k in keys], value)
|
|
239
247
|
|
|
248
|
+
|
|
240
249
|
def last_restapi_key_transformer(key, attr_desc, value):
|
|
241
250
|
"""A key transformer that returns the last RestAPI key.
|
|
242
251
|
|
|
@@ -248,23 +257,25 @@ def last_restapi_key_transformer(key, attr_desc, value):
|
|
|
248
257
|
key, value = full_restapi_key_transformer(key, attr_desc, value)
|
|
249
258
|
return (key[-1], value)
|
|
250
259
|
|
|
260
|
+
|
|
251
261
|
def _create_xml_node(tag, prefix=None, ns=None):
|
|
252
262
|
"""Create a XML node."""
|
|
253
263
|
if prefix and ns:
|
|
254
264
|
ET.register_namespace(prefix, ns)
|
|
255
265
|
if ns:
|
|
256
|
-
return ET.Element("{"+ns+"}"+tag)
|
|
266
|
+
return ET.Element("{" + ns + "}" + tag)
|
|
257
267
|
else:
|
|
258
268
|
return ET.Element(tag)
|
|
259
269
|
|
|
270
|
+
|
|
260
271
|
class Model(object):
|
|
261
272
|
"""Mixin for all client request body/response body models to support
|
|
262
273
|
serialization and deserialization.
|
|
263
274
|
"""
|
|
264
275
|
|
|
265
|
-
_subtype_map
|
|
266
|
-
_attribute_map
|
|
267
|
-
_validation
|
|
276
|
+
_subtype_map: Dict[str, Dict[str, Any]] = {}
|
|
277
|
+
_attribute_map: Dict[str, Dict[str, Any]] = {}
|
|
278
|
+
_validation: Dict[str, Dict[str, Any]] = {}
|
|
268
279
|
|
|
269
280
|
def __init__(self, **kwargs):
|
|
270
281
|
self.additional_properties = {}
|
|
@@ -291,30 +302,25 @@ class Model(object):
|
|
|
291
302
|
|
|
292
303
|
@classmethod
|
|
293
304
|
def enable_additional_properties_sending(cls):
|
|
294
|
-
cls._attribute_map[
|
|
305
|
+
cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"}
|
|
295
306
|
|
|
296
307
|
@classmethod
|
|
297
308
|
def is_xml_model(cls):
|
|
298
309
|
try:
|
|
299
|
-
cls._xml_map
|
|
310
|
+
cls._xml_map # type: ignore
|
|
300
311
|
except AttributeError:
|
|
301
312
|
return False
|
|
302
313
|
return True
|
|
303
314
|
|
|
304
315
|
@classmethod
|
|
305
316
|
def _create_xml_node(cls):
|
|
306
|
-
"""Create XML node.
|
|
307
|
-
"""
|
|
317
|
+
"""Create XML node."""
|
|
308
318
|
try:
|
|
309
|
-
xml_map = cls._xml_map
|
|
319
|
+
xml_map = cls._xml_map # type: ignore
|
|
310
320
|
except AttributeError:
|
|
311
321
|
xml_map = {}
|
|
312
322
|
|
|
313
|
-
return _create_xml_node(
|
|
314
|
-
xml_map.get('name', cls.__name__),
|
|
315
|
-
xml_map.get("prefix", None),
|
|
316
|
-
xml_map.get("ns", None)
|
|
317
|
-
)
|
|
323
|
+
return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None))
|
|
318
324
|
|
|
319
325
|
def serialize(self, keep_readonly=False, **kwargs):
|
|
320
326
|
"""Return the JSON that would be sent to azure from this model.
|
|
@@ -367,7 +373,7 @@ class Model(object):
|
|
|
367
373
|
@classmethod
|
|
368
374
|
def _infer_class_models(cls):
|
|
369
375
|
try:
|
|
370
|
-
str_models = cls.__module__.rsplit(
|
|
376
|
+
str_models = cls.__module__.rsplit(".", 1)[0]
|
|
371
377
|
models = sys.modules[str_models]
|
|
372
378
|
client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)}
|
|
373
379
|
if cls.__name__ not in client_models:
|
|
@@ -403,16 +409,20 @@ class Model(object):
|
|
|
403
409
|
:raises: DeserializationError if something went wrong
|
|
404
410
|
"""
|
|
405
411
|
deserializer = Deserializer(cls._infer_class_models())
|
|
406
|
-
deserializer.key_extractors =
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
412
|
+
deserializer.key_extractors = (
|
|
413
|
+
[
|
|
414
|
+
attribute_key_case_insensitive_extractor,
|
|
415
|
+
rest_key_case_insensitive_extractor,
|
|
416
|
+
last_rest_key_case_insensitive_extractor,
|
|
417
|
+
]
|
|
418
|
+
if key_extractors is None
|
|
419
|
+
else key_extractors
|
|
420
|
+
)
|
|
411
421
|
return deserializer(cls.__name__, data, content_type=content_type)
|
|
412
422
|
|
|
413
423
|
@classmethod
|
|
414
424
|
def _flatten_subtype(cls, key, objects):
|
|
415
|
-
if
|
|
425
|
+
if "_subtype_map" not in cls.__dict__:
|
|
416
426
|
return {}
|
|
417
427
|
result = dict(cls._subtype_map[key])
|
|
418
428
|
for valuetype in cls._subtype_map[key].values():
|
|
@@ -425,18 +435,14 @@ class Model(object):
|
|
|
425
435
|
We want to ignore any inherited _subtype_maps.
|
|
426
436
|
Remove the polymorphic key from the initial data.
|
|
427
437
|
"""
|
|
428
|
-
for subtype_key in cls.__dict__.get(
|
|
438
|
+
for subtype_key in cls.__dict__.get("_subtype_map", {}).keys():
|
|
429
439
|
subtype_value = None
|
|
430
440
|
|
|
431
441
|
if not isinstance(response, ET.Element):
|
|
432
442
|
rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1]
|
|
433
443
|
subtype_value = response.pop(rest_api_response_key, None) or response.pop(subtype_key, None)
|
|
434
444
|
else:
|
|
435
|
-
subtype_value = xml_key_extractor(
|
|
436
|
-
subtype_key,
|
|
437
|
-
cls._attribute_map[subtype_key],
|
|
438
|
-
response
|
|
439
|
-
)
|
|
445
|
+
subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response)
|
|
440
446
|
if subtype_value:
|
|
441
447
|
# Try to match base class. Can be class name only
|
|
442
448
|
# (bug to fix in Autorest to support x-ms-discriminator-name)
|
|
@@ -444,7 +450,7 @@ class Model(object):
|
|
|
444
450
|
return cls
|
|
445
451
|
flatten_mapping_type = cls._flatten_subtype(subtype_key, objects)
|
|
446
452
|
try:
|
|
447
|
-
return objects[flatten_mapping_type[subtype_value]]
|
|
453
|
+
return objects[flatten_mapping_type[subtype_value]] # type: ignore
|
|
448
454
|
except KeyError:
|
|
449
455
|
_LOGGER.warning(
|
|
450
456
|
"Subtype value %s has no mapping, use base class %s.",
|
|
@@ -453,11 +459,7 @@ class Model(object):
|
|
|
453
459
|
)
|
|
454
460
|
break
|
|
455
461
|
else:
|
|
456
|
-
_LOGGER.warning(
|
|
457
|
-
"Discriminator %s is absent or null, use base class %s.",
|
|
458
|
-
subtype_key,
|
|
459
|
-
cls.__name__
|
|
460
|
-
)
|
|
462
|
+
_LOGGER.warning("Discriminator %s is absent or null, use base class %s.", subtype_key, cls.__name__)
|
|
461
463
|
break
|
|
462
464
|
return cls
|
|
463
465
|
|
|
@@ -468,29 +470,40 @@ class Model(object):
|
|
|
468
470
|
:returns: A list of RestAPI part
|
|
469
471
|
:rtype: list
|
|
470
472
|
"""
|
|
471
|
-
rest_split_key = _FLATTEN.split(cls._attribute_map[attr_key][
|
|
473
|
+
rest_split_key = _FLATTEN.split(cls._attribute_map[attr_key]["key"])
|
|
472
474
|
return [_decode_attribute_map_key(key_part) for key_part in rest_split_key]
|
|
473
475
|
|
|
474
476
|
|
|
475
477
|
def _decode_attribute_map_key(key):
|
|
476
478
|
"""This decode a key in an _attribute_map to the actual key we want to look at
|
|
477
|
-
|
|
479
|
+
inside the received data.
|
|
478
480
|
|
|
479
|
-
|
|
481
|
+
:param str key: A key string from the generated code
|
|
480
482
|
"""
|
|
481
|
-
return key.replace(
|
|
483
|
+
return key.replace("\\.", ".")
|
|
482
484
|
|
|
483
485
|
|
|
484
486
|
class Serializer(object):
|
|
485
487
|
"""Request object model serializer."""
|
|
486
488
|
|
|
487
|
-
basic_types = {str:
|
|
488
|
-
|
|
489
|
-
_xml_basic_types_serializers = {
|
|
490
|
-
days = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu",
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
489
|
+
basic_types = {str: "str", int: "int", bool: "bool", float: "float"}
|
|
490
|
+
|
|
491
|
+
_xml_basic_types_serializers = {"bool": lambda x: str(x).lower()}
|
|
492
|
+
days = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun"}
|
|
493
|
+
months = {
|
|
494
|
+
1: "Jan",
|
|
495
|
+
2: "Feb",
|
|
496
|
+
3: "Mar",
|
|
497
|
+
4: "Apr",
|
|
498
|
+
5: "May",
|
|
499
|
+
6: "Jun",
|
|
500
|
+
7: "Jul",
|
|
501
|
+
8: "Aug",
|
|
502
|
+
9: "Sep",
|
|
503
|
+
10: "Oct",
|
|
504
|
+
11: "Nov",
|
|
505
|
+
12: "Dec",
|
|
506
|
+
}
|
|
494
507
|
validation = {
|
|
495
508
|
"min_length": lambda x, y: len(x) < y,
|
|
496
509
|
"max_length": lambda x, y: len(x) > y,
|
|
@@ -502,25 +515,25 @@ class Serializer(object):
|
|
|
502
515
|
"max_items": lambda x, y: len(x) > y,
|
|
503
516
|
"pattern": lambda x, y: not re.match(y, x, re.UNICODE),
|
|
504
517
|
"unique": lambda x, y: len(x) != len(set(x)),
|
|
505
|
-
"multiple": lambda x, y: x % y != 0
|
|
506
|
-
|
|
518
|
+
"multiple": lambda x, y: x % y != 0,
|
|
519
|
+
}
|
|
507
520
|
|
|
508
521
|
def __init__(self, classes=None):
|
|
509
522
|
self.serialize_type = {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
523
|
+
"iso-8601": Serializer.serialize_iso,
|
|
524
|
+
"rfc-1123": Serializer.serialize_rfc,
|
|
525
|
+
"unix-time": Serializer.serialize_unix,
|
|
526
|
+
"duration": Serializer.serialize_duration,
|
|
527
|
+
"date": Serializer.serialize_date,
|
|
528
|
+
"time": Serializer.serialize_time,
|
|
529
|
+
"decimal": Serializer.serialize_decimal,
|
|
530
|
+
"long": Serializer.serialize_long,
|
|
531
|
+
"bytearray": Serializer.serialize_bytearray,
|
|
532
|
+
"base64": Serializer.serialize_base64,
|
|
533
|
+
"object": self.serialize_object,
|
|
534
|
+
"[]": self.serialize_iter,
|
|
535
|
+
"{}": self.serialize_dict,
|
|
536
|
+
}
|
|
524
537
|
self.dependencies = dict(classes) if classes else {}
|
|
525
538
|
self.key_transformer = full_restapi_key_transformer
|
|
526
539
|
self.client_side_validation = True
|
|
@@ -542,14 +555,12 @@ class Serializer(object):
|
|
|
542
555
|
class_name = target_obj.__class__.__name__
|
|
543
556
|
|
|
544
557
|
if data_type:
|
|
545
|
-
return self.serialize_data(
|
|
546
|
-
target_obj, data_type, **kwargs)
|
|
558
|
+
return self.serialize_data(target_obj, data_type, **kwargs)
|
|
547
559
|
|
|
548
560
|
if not hasattr(target_obj, "_attribute_map"):
|
|
549
561
|
data_type = type(target_obj).__name__
|
|
550
562
|
if data_type in self.basic_types.values():
|
|
551
|
-
return self.serialize_data(
|
|
552
|
-
target_obj, data_type, **kwargs)
|
|
563
|
+
return self.serialize_data(target_obj, data_type, **kwargs)
|
|
553
564
|
|
|
554
565
|
# Force "is_xml" kwargs if we detect a XML model
|
|
555
566
|
try:
|
|
@@ -564,79 +575,72 @@ class Serializer(object):
|
|
|
564
575
|
attributes = target_obj._attribute_map
|
|
565
576
|
for attr, attr_desc in attributes.items():
|
|
566
577
|
attr_name = attr
|
|
567
|
-
if not keep_readonly and target_obj._validation.get(attr_name, {}).get(
|
|
578
|
+
if not keep_readonly and target_obj._validation.get(attr_name, {}).get("readonly", False):
|
|
568
579
|
continue
|
|
569
580
|
|
|
570
|
-
if attr_name == "additional_properties" and attr_desc["key"] ==
|
|
581
|
+
if attr_name == "additional_properties" and attr_desc["key"] == "":
|
|
571
582
|
if target_obj.additional_properties is not None:
|
|
572
583
|
serialized.update(target_obj.additional_properties)
|
|
573
584
|
continue
|
|
574
585
|
try:
|
|
575
|
-
|
|
586
|
+
|
|
576
587
|
orig_attr = getattr(target_obj, attr)
|
|
577
588
|
if is_xml_model_serialization:
|
|
578
|
-
pass
|
|
579
|
-
else:
|
|
589
|
+
pass # Don't provide "transformer" for XML for now. Keep "orig_attr"
|
|
590
|
+
else: # JSON
|
|
580
591
|
keys, orig_attr = key_transformer(attr, attr_desc.copy(), orig_attr)
|
|
581
592
|
keys = keys if isinstance(keys, list) else [keys]
|
|
582
593
|
|
|
583
|
-
### Serialize this data ###
|
|
584
594
|
kwargs["serialization_ctxt"] = attr_desc
|
|
585
|
-
new_attr = self.serialize_data(orig_attr, attr_desc[
|
|
595
|
+
new_attr = self.serialize_data(orig_attr, attr_desc["type"], **kwargs)
|
|
586
596
|
|
|
587
|
-
### Incorporate this data in the right place ###
|
|
588
597
|
if is_xml_model_serialization:
|
|
589
|
-
xml_desc = attr_desc.get(
|
|
590
|
-
xml_name = xml_desc.get(
|
|
591
|
-
xml_prefix = xml_desc.get(
|
|
592
|
-
xml_ns = xml_desc.get(
|
|
598
|
+
xml_desc = attr_desc.get("xml", {})
|
|
599
|
+
xml_name = xml_desc.get("name", attr_desc["key"])
|
|
600
|
+
xml_prefix = xml_desc.get("prefix", None)
|
|
601
|
+
xml_ns = xml_desc.get("ns", None)
|
|
593
602
|
if xml_desc.get("attr", False):
|
|
594
603
|
if xml_ns:
|
|
595
604
|
ET.register_namespace(xml_prefix, xml_ns)
|
|
596
|
-
xml_name = "{
|
|
597
|
-
serialized.set(xml_name, new_attr)
|
|
605
|
+
xml_name = "{}{}".format(xml_ns, xml_name)
|
|
606
|
+
serialized.set(xml_name, new_attr) # type: ignore
|
|
598
607
|
continue
|
|
599
608
|
if xml_desc.get("text", False):
|
|
600
|
-
serialized.text = new_attr
|
|
609
|
+
serialized.text = new_attr # type: ignore
|
|
601
610
|
continue
|
|
602
611
|
if isinstance(new_attr, list):
|
|
603
|
-
serialized.extend(new_attr)
|
|
612
|
+
serialized.extend(new_attr) # type: ignore
|
|
604
613
|
elif isinstance(new_attr, ET.Element):
|
|
605
614
|
# If the down XML has no XML/Name, we MUST replace the tag with the local tag. But keeping the namespaces.
|
|
606
|
-
if
|
|
615
|
+
if "name" not in getattr(orig_attr, "_xml_map", {}):
|
|
607
616
|
splitted_tag = new_attr.tag.split("}")
|
|
608
|
-
if len(splitted_tag) == 2:
|
|
617
|
+
if len(splitted_tag) == 2: # Namespace
|
|
609
618
|
new_attr.tag = "}".join([splitted_tag[0], xml_name])
|
|
610
619
|
else:
|
|
611
620
|
new_attr.tag = xml_name
|
|
612
|
-
serialized.append(new_attr)
|
|
621
|
+
serialized.append(new_attr) # type: ignore
|
|
613
622
|
else: # That's a basic type
|
|
614
623
|
# Integrate namespace if necessary
|
|
615
|
-
local_node = _create_xml_node(
|
|
616
|
-
xml_name,
|
|
617
|
-
xml_prefix,
|
|
618
|
-
xml_ns
|
|
619
|
-
)
|
|
624
|
+
local_node = _create_xml_node(xml_name, xml_prefix, xml_ns)
|
|
620
625
|
local_node.text = unicode_str(new_attr)
|
|
621
|
-
serialized.append(local_node)
|
|
622
|
-
else:
|
|
623
|
-
for k in reversed(keys):
|
|
626
|
+
serialized.append(local_node) # type: ignore
|
|
627
|
+
else: # JSON
|
|
628
|
+
for k in reversed(keys): # type: ignore
|
|
624
629
|
unflattened = {k: new_attr}
|
|
625
630
|
new_attr = unflattened
|
|
626
631
|
|
|
627
632
|
_new_attr = new_attr
|
|
628
633
|
_serialized = serialized
|
|
629
|
-
for k in keys:
|
|
634
|
+
for k in keys: # type: ignore
|
|
630
635
|
if k not in _serialized:
|
|
631
|
-
_serialized.update(_new_attr)
|
|
632
|
-
_new_attr = _new_attr[k]
|
|
636
|
+
_serialized.update(_new_attr) # type: ignore
|
|
637
|
+
_new_attr = _new_attr[k] # type: ignore
|
|
633
638
|
_serialized = _serialized[k]
|
|
634
639
|
except ValueError:
|
|
635
640
|
continue
|
|
636
641
|
|
|
637
642
|
except (AttributeError, KeyError, TypeError) as err:
|
|
638
|
-
msg = "Attribute {} in object {} cannot be serialized.\n{}".format(
|
|
639
|
-
attr_name, class_name, str(target_obj))
|
|
643
|
+
msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj))
|
|
640
644
|
raise_with_traceback(SerializationError, msg, err)
|
|
641
645
|
else:
|
|
642
646
|
return serialized
|
|
@@ -652,7 +656,7 @@ class Serializer(object):
|
|
|
652
656
|
"""
|
|
653
657
|
|
|
654
658
|
# Just in case this is a dict
|
|
655
|
-
internal_data_type = data_type.strip(
|
|
659
|
+
internal_data_type = data_type.strip("[]{}")
|
|
656
660
|
internal_data_type = self.dependencies.get(internal_data_type, None)
|
|
657
661
|
try:
|
|
658
662
|
is_xml_model_serialization = kwargs["is_xml"]
|
|
@@ -668,19 +672,18 @@ class Serializer(object):
|
|
|
668
672
|
# We're not able to deal with additional properties for now.
|
|
669
673
|
deserializer.additional_properties_detection = False
|
|
670
674
|
if is_xml_model_serialization:
|
|
671
|
-
deserializer.key_extractors = [
|
|
675
|
+
deserializer.key_extractors = [ # type: ignore
|
|
672
676
|
attribute_key_case_insensitive_extractor,
|
|
673
677
|
]
|
|
674
678
|
else:
|
|
675
679
|
deserializer.key_extractors = [
|
|
676
680
|
rest_key_case_insensitive_extractor,
|
|
677
681
|
attribute_key_case_insensitive_extractor,
|
|
678
|
-
last_rest_key_case_insensitive_extractor
|
|
682
|
+
last_rest_key_case_insensitive_extractor,
|
|
679
683
|
]
|
|
680
684
|
data = deserializer._deserialize(data_type, data)
|
|
681
685
|
except DeserializationError as err:
|
|
682
|
-
raise_with_traceback(
|
|
683
|
-
SerializationError, "Unable to build a model: "+str(err), err)
|
|
686
|
+
raise_with_traceback(SerializationError, "Unable to build a model: " + str(err), err)
|
|
684
687
|
|
|
685
688
|
return self._serialize(data, data_type, **kwargs)
|
|
686
689
|
|
|
@@ -695,13 +698,13 @@ class Serializer(object):
|
|
|
695
698
|
"""
|
|
696
699
|
try:
|
|
697
700
|
output = self.serialize_data(data, data_type, **kwargs)
|
|
698
|
-
if data_type ==
|
|
701
|
+
if data_type == "bool":
|
|
699
702
|
output = json.dumps(output)
|
|
700
703
|
|
|
701
|
-
if kwargs.get(
|
|
704
|
+
if kwargs.get("skip_quote") is True:
|
|
702
705
|
output = str(output)
|
|
703
706
|
else:
|
|
704
|
-
output = quote(str(output), safe=
|
|
707
|
+
output = quote(str(output), safe="")
|
|
705
708
|
except SerializationError:
|
|
706
709
|
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
707
710
|
else:
|
|
@@ -720,27 +723,19 @@ class Serializer(object):
|
|
|
720
723
|
# Treat the list aside, since we don't want to encode the div separator
|
|
721
724
|
if data_type.startswith("["):
|
|
722
725
|
internal_data_type = data_type[1:-1]
|
|
723
|
-
data = [
|
|
724
|
-
|
|
725
|
-
for d
|
|
726
|
-
in data
|
|
727
|
-
]
|
|
728
|
-
if not kwargs.get('skip_quote', False):
|
|
729
|
-
data = [
|
|
730
|
-
quote(str(d), safe='')
|
|
731
|
-
for d
|
|
732
|
-
in data
|
|
733
|
-
]
|
|
726
|
+
data = [self.serialize_data(d, internal_data_type, **kwargs) if d is not None else "" for d in data]
|
|
727
|
+
if not kwargs.get("skip_quote", False):
|
|
728
|
+
data = [quote(str(d), safe="") for d in data]
|
|
734
729
|
return str(self.serialize_iter(data, internal_data_type, **kwargs))
|
|
735
730
|
|
|
736
731
|
# Not a list, regular serialization
|
|
737
732
|
output = self.serialize_data(data, data_type, **kwargs)
|
|
738
|
-
if data_type ==
|
|
733
|
+
if data_type == "bool":
|
|
739
734
|
output = json.dumps(output)
|
|
740
|
-
if kwargs.get(
|
|
735
|
+
if kwargs.get("skip_quote") is True:
|
|
741
736
|
output = str(output)
|
|
742
737
|
else:
|
|
743
|
-
output = quote(str(output), safe=
|
|
738
|
+
output = quote(str(output), safe="")
|
|
744
739
|
except SerializationError:
|
|
745
740
|
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
746
741
|
else:
|
|
@@ -756,11 +751,11 @@ class Serializer(object):
|
|
|
756
751
|
:raises: ValueError if data is None
|
|
757
752
|
"""
|
|
758
753
|
try:
|
|
759
|
-
if data_type in [
|
|
754
|
+
if data_type in ["[str]"]:
|
|
760
755
|
data = ["" if d is None else d for d in data]
|
|
761
756
|
|
|
762
757
|
output = self.serialize_data(data, data_type, **kwargs)
|
|
763
|
-
if data_type ==
|
|
758
|
+
if data_type == "bool":
|
|
764
759
|
output = json.dumps(output)
|
|
765
760
|
except SerializationError:
|
|
766
761
|
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
@@ -796,13 +791,11 @@ class Serializer(object):
|
|
|
796
791
|
|
|
797
792
|
iter_type = data_type[0] + data_type[-1]
|
|
798
793
|
if iter_type in self.serialize_type:
|
|
799
|
-
return self.serialize_type[iter_type](
|
|
800
|
-
data, data_type[1:-1], **kwargs)
|
|
794
|
+
return self.serialize_type[iter_type](data, data_type[1:-1], **kwargs)
|
|
801
795
|
|
|
802
796
|
except (ValueError, TypeError) as err:
|
|
803
797
|
msg = "Unable to serialize value: {!r} as type: {!r}."
|
|
804
|
-
raise_with_traceback(
|
|
805
|
-
SerializationError, msg.format(data, data_type), err)
|
|
798
|
+
raise_with_traceback(SerializationError, msg.format(data, data_type), err)
|
|
806
799
|
else:
|
|
807
800
|
return self._serialize(data, **kwargs)
|
|
808
801
|
|
|
@@ -829,7 +822,7 @@ class Serializer(object):
|
|
|
829
822
|
custom_serializer = cls._get_custom_serializers(data_type, **kwargs)
|
|
830
823
|
if custom_serializer:
|
|
831
824
|
return custom_serializer(data)
|
|
832
|
-
if data_type ==
|
|
825
|
+
if data_type == "str":
|
|
833
826
|
return cls.serialize_unicode(data)
|
|
834
827
|
return eval(data_type)(data) # nosec
|
|
835
828
|
|
|
@@ -847,7 +840,7 @@ class Serializer(object):
|
|
|
847
840
|
pass
|
|
848
841
|
|
|
849
842
|
try:
|
|
850
|
-
if isinstance(data, unicode):
|
|
843
|
+
if isinstance(data, unicode): # type: ignore
|
|
851
844
|
# Don't change it, JSON and XML ElementTree are totally able
|
|
852
845
|
# to serialize correctly u'' strings
|
|
853
846
|
return data
|
|
@@ -886,25 +879,21 @@ class Serializer(object):
|
|
|
886
879
|
serialized.append(None)
|
|
887
880
|
|
|
888
881
|
if div:
|
|
889
|
-
serialized = [
|
|
882
|
+
serialized = ["" if s is None else str(s) for s in serialized]
|
|
890
883
|
serialized = div.join(serialized)
|
|
891
884
|
|
|
892
|
-
if
|
|
885
|
+
if "xml" in serialization_ctxt or is_xml:
|
|
893
886
|
# XML serialization is more complicated
|
|
894
|
-
xml_desc = serialization_ctxt.get(
|
|
895
|
-
xml_name = xml_desc.get(
|
|
887
|
+
xml_desc = serialization_ctxt.get("xml", {})
|
|
888
|
+
xml_name = xml_desc.get("name")
|
|
896
889
|
if not xml_name:
|
|
897
|
-
xml_name = serialization_ctxt[
|
|
890
|
+
xml_name = serialization_ctxt["key"]
|
|
898
891
|
|
|
899
892
|
# Create a wrap node if necessary (use the fact that Element and list have "append")
|
|
900
893
|
is_wrapped = xml_desc.get("wrapped", False)
|
|
901
894
|
node_name = xml_desc.get("itemsName", xml_name)
|
|
902
895
|
if is_wrapped:
|
|
903
|
-
final_result = _create_xml_node(
|
|
904
|
-
xml_name,
|
|
905
|
-
xml_desc.get('prefix', None),
|
|
906
|
-
xml_desc.get('ns', None)
|
|
907
|
-
)
|
|
896
|
+
final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None))
|
|
908
897
|
else:
|
|
909
898
|
final_result = []
|
|
910
899
|
# All list elements to "local_node"
|
|
@@ -912,11 +901,7 @@ class Serializer(object):
|
|
|
912
901
|
if isinstance(el, ET.Element):
|
|
913
902
|
el_node = el
|
|
914
903
|
else:
|
|
915
|
-
el_node = _create_xml_node(
|
|
916
|
-
node_name,
|
|
917
|
-
xml_desc.get('prefix', None),
|
|
918
|
-
xml_desc.get('ns', None)
|
|
919
|
-
)
|
|
904
|
+
el_node = _create_xml_node(node_name, xml_desc.get("prefix", None), xml_desc.get("ns", None))
|
|
920
905
|
if el is not None: # Otherwise it writes "None" :-p
|
|
921
906
|
el_node.text = str(el)
|
|
922
907
|
final_result.append(el_node)
|
|
@@ -936,21 +921,16 @@ class Serializer(object):
|
|
|
936
921
|
serialized = {}
|
|
937
922
|
for key, value in attr.items():
|
|
938
923
|
try:
|
|
939
|
-
serialized[self.serialize_unicode(key)] = self.serialize_data(
|
|
940
|
-
value, dict_type, **kwargs)
|
|
924
|
+
serialized[self.serialize_unicode(key)] = self.serialize_data(value, dict_type, **kwargs)
|
|
941
925
|
except ValueError:
|
|
942
926
|
serialized[self.serialize_unicode(key)] = None
|
|
943
927
|
|
|
944
|
-
if
|
|
928
|
+
if "xml" in serialization_ctxt:
|
|
945
929
|
# XML serialization is more complicated
|
|
946
|
-
xml_desc = serialization_ctxt[
|
|
947
|
-
xml_name = xml_desc[
|
|
930
|
+
xml_desc = serialization_ctxt["xml"]
|
|
931
|
+
xml_name = xml_desc["name"]
|
|
948
932
|
|
|
949
|
-
final_result = _create_xml_node(
|
|
950
|
-
xml_name,
|
|
951
|
-
xml_desc.get('prefix', None),
|
|
952
|
-
xml_desc.get('ns', None)
|
|
953
|
-
)
|
|
933
|
+
final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None))
|
|
954
934
|
for key, value in serialized.items():
|
|
955
935
|
ET.SubElement(final_result, key).text = value
|
|
956
936
|
return final_result
|
|
@@ -996,8 +976,7 @@ class Serializer(object):
|
|
|
996
976
|
serialized = {}
|
|
997
977
|
for key, value in attr.items():
|
|
998
978
|
try:
|
|
999
|
-
serialized[self.serialize_unicode(key)] = self.serialize_object(
|
|
1000
|
-
value, **kwargs)
|
|
979
|
+
serialized[self.serialize_unicode(key)] = self.serialize_object(value, **kwargs)
|
|
1001
980
|
except ValueError:
|
|
1002
981
|
serialized[self.serialize_unicode(key)] = None
|
|
1003
982
|
return serialized
|
|
@@ -1006,8 +985,7 @@ class Serializer(object):
|
|
|
1006
985
|
serialized = []
|
|
1007
986
|
for obj in attr:
|
|
1008
987
|
try:
|
|
1009
|
-
serialized.append(self.serialize_object(
|
|
1010
|
-
obj, **kwargs))
|
|
988
|
+
serialized.append(self.serialize_object(obj, **kwargs))
|
|
1011
989
|
except ValueError:
|
|
1012
990
|
pass
|
|
1013
991
|
return serialized
|
|
@@ -1020,10 +998,10 @@ class Serializer(object):
|
|
|
1020
998
|
except AttributeError:
|
|
1021
999
|
result = attr
|
|
1022
1000
|
try:
|
|
1023
|
-
enum_obj(result)
|
|
1001
|
+
enum_obj(result) # type: ignore
|
|
1024
1002
|
return result
|
|
1025
1003
|
except ValueError:
|
|
1026
|
-
for enum_value in enum_obj:
|
|
1004
|
+
for enum_value in enum_obj: # type: ignore
|
|
1027
1005
|
if enum_value.value.lower() == str(attr).lower():
|
|
1028
1006
|
return enum_value.value
|
|
1029
1007
|
error = "{!r} is not valid value for enum {!r}"
|
|
@@ -1045,8 +1023,8 @@ class Serializer(object):
|
|
|
1045
1023
|
:param attr: Object to be serialized.
|
|
1046
1024
|
:rtype: str
|
|
1047
1025
|
"""
|
|
1048
|
-
encoded = b64encode(attr).decode(
|
|
1049
|
-
return encoded.strip(
|
|
1026
|
+
encoded = b64encode(attr).decode("ascii")
|
|
1027
|
+
return encoded.strip("=").replace("+", "-").replace("/", "_")
|
|
1050
1028
|
|
|
1051
1029
|
@staticmethod
|
|
1052
1030
|
def serialize_decimal(attr, **kwargs):
|
|
@@ -1113,16 +1091,20 @@ class Serializer(object):
|
|
|
1113
1091
|
"""
|
|
1114
1092
|
try:
|
|
1115
1093
|
if not attr.tzinfo:
|
|
1116
|
-
_LOGGER.warning(
|
|
1117
|
-
"Datetime with no tzinfo will be considered UTC.")
|
|
1094
|
+
_LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
|
|
1118
1095
|
utc = attr.utctimetuple()
|
|
1119
1096
|
except AttributeError:
|
|
1120
1097
|
raise TypeError("RFC1123 object must be valid Datetime object.")
|
|
1121
1098
|
|
|
1122
1099
|
return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format(
|
|
1123
|
-
Serializer.days[utc.tm_wday],
|
|
1124
|
-
|
|
1125
|
-
|
|
1100
|
+
Serializer.days[utc.tm_wday],
|
|
1101
|
+
utc.tm_mday,
|
|
1102
|
+
Serializer.months[utc.tm_mon],
|
|
1103
|
+
utc.tm_year,
|
|
1104
|
+
utc.tm_hour,
|
|
1105
|
+
utc.tm_min,
|
|
1106
|
+
utc.tm_sec,
|
|
1107
|
+
)
|
|
1126
1108
|
|
|
1127
1109
|
@staticmethod
|
|
1128
1110
|
def serialize_iso(attr, **kwargs):
|
|
@@ -1136,19 +1118,18 @@ class Serializer(object):
|
|
|
1136
1118
|
attr = isodate.parse_datetime(attr)
|
|
1137
1119
|
try:
|
|
1138
1120
|
if not attr.tzinfo:
|
|
1139
|
-
_LOGGER.warning(
|
|
1140
|
-
"Datetime with no tzinfo will be considered UTC.")
|
|
1121
|
+
_LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
|
|
1141
1122
|
utc = attr.utctimetuple()
|
|
1142
1123
|
if utc.tm_year > 9999 or utc.tm_year < 1:
|
|
1143
1124
|
raise OverflowError("Hit max or min date")
|
|
1144
1125
|
|
|
1145
|
-
microseconds = str(attr.microsecond).rjust(6,
|
|
1126
|
+
microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0")
|
|
1146
1127
|
if microseconds:
|
|
1147
|
-
microseconds =
|
|
1128
|
+
microseconds = "." + microseconds
|
|
1148
1129
|
date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format(
|
|
1149
|
-
utc.tm_year, utc.tm_mon, utc.tm_mday,
|
|
1150
|
-
|
|
1151
|
-
return date + microseconds +
|
|
1130
|
+
utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec
|
|
1131
|
+
)
|
|
1132
|
+
return date + microseconds + "Z"
|
|
1152
1133
|
except (ValueError, OverflowError) as err:
|
|
1153
1134
|
msg = "Unable to serialize datetime object."
|
|
1154
1135
|
raise_with_traceback(SerializationError, msg, err)
|
|
@@ -1169,17 +1150,17 @@ class Serializer(object):
|
|
|
1169
1150
|
return attr
|
|
1170
1151
|
try:
|
|
1171
1152
|
if not attr.tzinfo:
|
|
1172
|
-
_LOGGER.warning(
|
|
1173
|
-
"Datetime with no tzinfo will be considered UTC.")
|
|
1153
|
+
_LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
|
|
1174
1154
|
return int(calendar.timegm(attr.utctimetuple()))
|
|
1175
1155
|
except AttributeError:
|
|
1176
1156
|
raise TypeError("Unix time object must be valid Datetime object.")
|
|
1177
1157
|
|
|
1158
|
+
|
|
1178
1159
|
def rest_key_extractor(attr, attr_desc, data):
|
|
1179
|
-
key = attr_desc[
|
|
1160
|
+
key = attr_desc["key"]
|
|
1180
1161
|
working_data = data
|
|
1181
1162
|
|
|
1182
|
-
while
|
|
1163
|
+
while "." in key:
|
|
1183
1164
|
dict_keys = _FLATTEN.split(key)
|
|
1184
1165
|
if len(dict_keys) == 1:
|
|
1185
1166
|
key = _decode_attribute_map_key(dict_keys[0])
|
|
@@ -1191,15 +1172,16 @@ def rest_key_extractor(attr, attr_desc, data):
|
|
|
1191
1172
|
# that all properties under are None as well
|
|
1192
1173
|
# https://github.com/Azure/msrest-for-python/issues/197
|
|
1193
1174
|
return None
|
|
1194
|
-
key =
|
|
1175
|
+
key = ".".join(dict_keys[1:])
|
|
1195
1176
|
|
|
1196
1177
|
return working_data.get(key)
|
|
1197
1178
|
|
|
1179
|
+
|
|
1198
1180
|
def rest_key_case_insensitive_extractor(attr, attr_desc, data):
|
|
1199
|
-
key = attr_desc[
|
|
1181
|
+
key = attr_desc["key"]
|
|
1200
1182
|
working_data = data
|
|
1201
1183
|
|
|
1202
|
-
while
|
|
1184
|
+
while "." in key:
|
|
1203
1185
|
dict_keys = _FLATTEN.split(key)
|
|
1204
1186
|
if len(dict_keys) == 1:
|
|
1205
1187
|
key = _decode_attribute_map_key(dict_keys[0])
|
|
@@ -1211,30 +1193,33 @@ def rest_key_case_insensitive_extractor(attr, attr_desc, data):
|
|
|
1211
1193
|
# that all properties under are None as well
|
|
1212
1194
|
# https://github.com/Azure/msrest-for-python/issues/197
|
|
1213
1195
|
return None
|
|
1214
|
-
key =
|
|
1196
|
+
key = ".".join(dict_keys[1:])
|
|
1215
1197
|
|
|
1216
1198
|
if working_data:
|
|
1217
1199
|
return attribute_key_case_insensitive_extractor(key, None, working_data)
|
|
1218
1200
|
|
|
1201
|
+
|
|
1219
1202
|
def last_rest_key_extractor(attr, attr_desc, data):
|
|
1220
|
-
"""Extract the attribute in "data" based on the last part of the JSON path key.
|
|
1221
|
-
""
|
|
1222
|
-
key = attr_desc['key']
|
|
1203
|
+
"""Extract the attribute in "data" based on the last part of the JSON path key."""
|
|
1204
|
+
key = attr_desc["key"]
|
|
1223
1205
|
dict_keys = _FLATTEN.split(key)
|
|
1224
1206
|
return attribute_key_extractor(dict_keys[-1], None, data)
|
|
1225
1207
|
|
|
1208
|
+
|
|
1226
1209
|
def last_rest_key_case_insensitive_extractor(attr, attr_desc, data):
|
|
1227
1210
|
"""Extract the attribute in "data" based on the last part of the JSON path key.
|
|
1228
1211
|
|
|
1229
1212
|
This is the case insensitive version of "last_rest_key_extractor"
|
|
1230
1213
|
"""
|
|
1231
|
-
key = attr_desc[
|
|
1214
|
+
key = attr_desc["key"]
|
|
1232
1215
|
dict_keys = _FLATTEN.split(key)
|
|
1233
1216
|
return attribute_key_case_insensitive_extractor(dict_keys[-1], None, data)
|
|
1234
1217
|
|
|
1218
|
+
|
|
1235
1219
|
def attribute_key_extractor(attr, _, data):
|
|
1236
1220
|
return data.get(attr)
|
|
1237
1221
|
|
|
1222
|
+
|
|
1238
1223
|
def attribute_key_case_insensitive_extractor(attr, _, data):
|
|
1239
1224
|
found_key = None
|
|
1240
1225
|
lower_attr = attr.lower()
|
|
@@ -1245,6 +1230,7 @@ def attribute_key_case_insensitive_extractor(attr, _, data):
|
|
|
1245
1230
|
|
|
1246
1231
|
return data.get(found_key)
|
|
1247
1232
|
|
|
1233
|
+
|
|
1248
1234
|
def _extract_name_from_internal_type(internal_type):
|
|
1249
1235
|
"""Given an internal type XML description, extract correct XML name with namespace.
|
|
1250
1236
|
|
|
@@ -1253,10 +1239,10 @@ def _extract_name_from_internal_type(internal_type):
|
|
|
1253
1239
|
:returns: A tuple XML name + namespace dict
|
|
1254
1240
|
"""
|
|
1255
1241
|
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
|
|
1256
|
-
xml_name = internal_type_xml_map.get(
|
|
1242
|
+
xml_name = internal_type_xml_map.get("name", internal_type.__name__)
|
|
1257
1243
|
xml_ns = internal_type_xml_map.get("ns", None)
|
|
1258
1244
|
if xml_ns:
|
|
1259
|
-
xml_name = "{
|
|
1245
|
+
xml_name = "{}{}".format(xml_ns, xml_name)
|
|
1260
1246
|
return xml_name
|
|
1261
1247
|
|
|
1262
1248
|
|
|
@@ -1268,19 +1254,19 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1268
1254
|
if not isinstance(data, ET.Element):
|
|
1269
1255
|
return None
|
|
1270
1256
|
|
|
1271
|
-
xml_desc = attr_desc.get(
|
|
1272
|
-
xml_name = xml_desc.get(
|
|
1257
|
+
xml_desc = attr_desc.get("xml", {})
|
|
1258
|
+
xml_name = xml_desc.get("name", attr_desc["key"])
|
|
1273
1259
|
|
|
1274
1260
|
# Look for a children
|
|
1275
|
-
is_iter_type = attr_desc[
|
|
1261
|
+
is_iter_type = attr_desc["type"].startswith("[")
|
|
1276
1262
|
is_wrapped = xml_desc.get("wrapped", False)
|
|
1277
1263
|
internal_type = attr_desc.get("internalType", None)
|
|
1278
1264
|
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
|
|
1279
1265
|
|
|
1280
1266
|
# Integrate namespace if necessary
|
|
1281
|
-
xml_ns = xml_desc.get(
|
|
1267
|
+
xml_ns = xml_desc.get("ns", internal_type_xml_map.get("ns", None))
|
|
1282
1268
|
if xml_ns:
|
|
1283
|
-
xml_name = "{
|
|
1269
|
+
xml_name = "{}{}".format(xml_ns, xml_name)
|
|
1284
1270
|
|
|
1285
1271
|
# If it's an attribute, that's simple
|
|
1286
1272
|
if xml_desc.get("attr", False):
|
|
@@ -1294,15 +1280,15 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1294
1280
|
# - Wrapped node
|
|
1295
1281
|
# - Internal type is an enum (considered basic types)
|
|
1296
1282
|
# - Internal type has no XML/Name node
|
|
1297
|
-
if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or
|
|
1283
|
+
if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or "name" not in internal_type_xml_map)):
|
|
1298
1284
|
children = data.findall(xml_name)
|
|
1299
1285
|
# If internal type has a local name and it's not a list, I use that name
|
|
1300
|
-
elif not is_iter_type and internal_type and
|
|
1286
|
+
elif not is_iter_type and internal_type and "name" in internal_type_xml_map:
|
|
1301
1287
|
xml_name = _extract_name_from_internal_type(internal_type)
|
|
1302
1288
|
children = data.findall(xml_name)
|
|
1303
1289
|
# That's an array
|
|
1304
1290
|
else:
|
|
1305
|
-
if internal_type:
|
|
1291
|
+
if internal_type: # Complex type, ignore itemsName and use the complex type name
|
|
1306
1292
|
items_name = _extract_name_from_internal_type(internal_type)
|
|
1307
1293
|
else:
|
|
1308
1294
|
items_name = xml_desc.get("itemsName", xml_name)
|
|
@@ -1311,21 +1297,22 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1311
1297
|
if len(children) == 0:
|
|
1312
1298
|
if is_iter_type:
|
|
1313
1299
|
if is_wrapped:
|
|
1314
|
-
return None
|
|
1300
|
+
return None # is_wrapped no node, we want None
|
|
1315
1301
|
else:
|
|
1316
|
-
return []
|
|
1302
|
+
return [] # not wrapped, assume empty list
|
|
1317
1303
|
return None # Assume it's not there, maybe an optional node.
|
|
1318
1304
|
|
|
1319
1305
|
# If is_iter_type and not wrapped, return all found children
|
|
1320
1306
|
if is_iter_type:
|
|
1321
1307
|
if not is_wrapped:
|
|
1322
1308
|
return children
|
|
1323
|
-
else:
|
|
1309
|
+
else: # Iter and wrapped, should have found one node only (the wrap one)
|
|
1324
1310
|
if len(children) != 1:
|
|
1325
1311
|
raise DeserializationError(
|
|
1326
1312
|
"Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format(
|
|
1327
1313
|
xml_name
|
|
1328
|
-
)
|
|
1314
|
+
)
|
|
1315
|
+
)
|
|
1329
1316
|
return list(children[0]) # Might be empty list and that's ok.
|
|
1330
1317
|
|
|
1331
1318
|
# Here it's not a itertype, we should have found one element only or empty
|
|
@@ -1333,6 +1320,7 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1333
1320
|
raise DeserializationError("Find several XML '{}' where it was not expected".format(xml_name))
|
|
1334
1321
|
return children[0]
|
|
1335
1322
|
|
|
1323
|
+
|
|
1336
1324
|
class Deserializer(object):
|
|
1337
1325
|
"""Response object model deserializer.
|
|
1338
1326
|
|
|
@@ -1340,37 +1328,32 @@ class Deserializer(object):
|
|
|
1340
1328
|
:ivar list key_extractors: Ordered list of extractors to be used by this deserializer.
|
|
1341
1329
|
"""
|
|
1342
1330
|
|
|
1343
|
-
basic_types = {str:
|
|
1331
|
+
basic_types = {str: "str", int: "int", bool: "bool", float: "float"}
|
|
1344
1332
|
|
|
1345
|
-
valid_date = re.compile(
|
|
1346
|
-
r'\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}'
|
|
1347
|
-
r'\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?')
|
|
1333
|
+
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}]?")
|
|
1348
1334
|
|
|
1349
1335
|
def __init__(self, classes=None):
|
|
1350
1336
|
self.deserialize_type = {
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1337
|
+
"iso-8601": Deserializer.deserialize_iso,
|
|
1338
|
+
"rfc-1123": Deserializer.deserialize_rfc,
|
|
1339
|
+
"unix-time": Deserializer.deserialize_unix,
|
|
1340
|
+
"duration": Deserializer.deserialize_duration,
|
|
1341
|
+
"date": Deserializer.deserialize_date,
|
|
1342
|
+
"time": Deserializer.deserialize_time,
|
|
1343
|
+
"decimal": Deserializer.deserialize_decimal,
|
|
1344
|
+
"long": Deserializer.deserialize_long,
|
|
1345
|
+
"bytearray": Deserializer.deserialize_bytearray,
|
|
1346
|
+
"base64": Deserializer.deserialize_base64,
|
|
1347
|
+
"object": self.deserialize_object,
|
|
1348
|
+
"[]": self.deserialize_iter,
|
|
1349
|
+
"{}": self.deserialize_dict,
|
|
1350
|
+
}
|
|
1365
1351
|
self.deserialize_expected_types = {
|
|
1366
|
-
|
|
1367
|
-
|
|
1352
|
+
"duration": (isodate.Duration, datetime.timedelta),
|
|
1353
|
+
"iso-8601": (datetime.datetime),
|
|
1368
1354
|
}
|
|
1369
1355
|
self.dependencies = dict(classes) if classes else {}
|
|
1370
|
-
self.key_extractors = [
|
|
1371
|
-
rest_key_extractor,
|
|
1372
|
-
xml_key_extractor
|
|
1373
|
-
]
|
|
1356
|
+
self.key_extractors = [rest_key_extractor, xml_key_extractor]
|
|
1374
1357
|
# Additional properties only works if the "rest_key_extractor" is used to
|
|
1375
1358
|
# extract the keys. Making it to work whatever the key extractor is too much
|
|
1376
1359
|
# complicated, with no real scenario for now.
|
|
@@ -1403,8 +1386,7 @@ class Deserializer(object):
|
|
|
1403
1386
|
"""
|
|
1404
1387
|
# This is already a model, go recursive just in case
|
|
1405
1388
|
if hasattr(data, "_attribute_map"):
|
|
1406
|
-
constants = [name for name, config in getattr(data,
|
|
1407
|
-
if config.get('constant')]
|
|
1389
|
+
constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")]
|
|
1408
1390
|
try:
|
|
1409
1391
|
for attr, mapconfig in data._attribute_map.items():
|
|
1410
1392
|
if attr in constants:
|
|
@@ -1412,15 +1394,11 @@ class Deserializer(object):
|
|
|
1412
1394
|
value = getattr(data, attr)
|
|
1413
1395
|
if value is None:
|
|
1414
1396
|
continue
|
|
1415
|
-
local_type = mapconfig[
|
|
1416
|
-
internal_data_type = local_type.strip(
|
|
1397
|
+
local_type = mapconfig["type"]
|
|
1398
|
+
internal_data_type = local_type.strip("[]{}")
|
|
1417
1399
|
if internal_data_type not in self.dependencies or isinstance(internal_data_type, Enum):
|
|
1418
1400
|
continue
|
|
1419
|
-
setattr(
|
|
1420
|
-
data,
|
|
1421
|
-
attr,
|
|
1422
|
-
self._deserialize(local_type, value)
|
|
1423
|
-
)
|
|
1401
|
+
setattr(data, attr, self._deserialize(local_type, value))
|
|
1424
1402
|
return data
|
|
1425
1403
|
except AttributeError:
|
|
1426
1404
|
return
|
|
@@ -1435,16 +1413,16 @@ class Deserializer(object):
|
|
|
1435
1413
|
if data is None:
|
|
1436
1414
|
return data
|
|
1437
1415
|
try:
|
|
1438
|
-
attributes = response._attribute_map
|
|
1416
|
+
attributes = response._attribute_map # type: ignore
|
|
1439
1417
|
d_attrs = {}
|
|
1440
1418
|
for attr, attr_desc in attributes.items():
|
|
1441
1419
|
# Check empty string. If it's not empty, someone has a real "additionalProperties"...
|
|
1442
|
-
if attr == "additional_properties" and attr_desc["key"] ==
|
|
1420
|
+
if attr == "additional_properties" and attr_desc["key"] == "":
|
|
1443
1421
|
continue
|
|
1444
1422
|
raw_value = None
|
|
1445
1423
|
# Enhance attr_desc with some dynamic data
|
|
1446
|
-
attr_desc = attr_desc.copy()
|
|
1447
|
-
internal_data_type = attr_desc["type"].strip(
|
|
1424
|
+
attr_desc = attr_desc.copy() # Do a copy, do not change the real one
|
|
1425
|
+
internal_data_type = attr_desc["type"].strip("[]{}")
|
|
1448
1426
|
if internal_data_type in self.dependencies:
|
|
1449
1427
|
attr_desc["internalType"] = self.dependencies[internal_data_type]
|
|
1450
1428
|
|
|
@@ -1452,21 +1430,18 @@ class Deserializer(object):
|
|
|
1452
1430
|
found_value = key_extractor(attr, attr_desc, data)
|
|
1453
1431
|
if found_value is not None:
|
|
1454
1432
|
if raw_value is not None and raw_value != found_value:
|
|
1455
|
-
msg = (
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
msg,
|
|
1459
|
-
found_value,
|
|
1460
|
-
key_extractor,
|
|
1461
|
-
attr
|
|
1433
|
+
msg = (
|
|
1434
|
+
"Ignoring extracted value '%s' from %s for key '%s'"
|
|
1435
|
+
" (duplicate extraction, follow extractors order)"
|
|
1462
1436
|
)
|
|
1437
|
+
_LOGGER.warning(msg, found_value, key_extractor, attr)
|
|
1463
1438
|
continue
|
|
1464
1439
|
raw_value = found_value
|
|
1465
1440
|
|
|
1466
|
-
value = self.deserialize_data(raw_value, attr_desc[
|
|
1441
|
+
value = self.deserialize_data(raw_value, attr_desc["type"])
|
|
1467
1442
|
d_attrs[attr] = value
|
|
1468
1443
|
except (AttributeError, TypeError, KeyError) as err:
|
|
1469
|
-
msg = "Unable to deserialize to object: " + class_name
|
|
1444
|
+
msg = "Unable to deserialize to object: " + class_name # type: ignore
|
|
1470
1445
|
raise_with_traceback(DeserializationError, msg, err)
|
|
1471
1446
|
else:
|
|
1472
1447
|
additional_properties = self._build_additional_properties(attributes, data)
|
|
@@ -1475,14 +1450,17 @@ class Deserializer(object):
|
|
|
1475
1450
|
def _build_additional_properties(self, attribute_map, data):
|
|
1476
1451
|
if not self.additional_properties_detection:
|
|
1477
1452
|
return None
|
|
1478
|
-
if "additional_properties" in attribute_map and attribute_map.get("additional_properties", {}).get("key") !=
|
|
1453
|
+
if "additional_properties" in attribute_map and attribute_map.get("additional_properties", {}).get("key") != "":
|
|
1479
1454
|
# Check empty string. If it's not empty, someone has a real "additionalProperties"
|
|
1480
1455
|
return None
|
|
1481
1456
|
if isinstance(data, ET.Element):
|
|
1482
1457
|
data = {el.tag: el.text for el in data}
|
|
1483
1458
|
|
|
1484
|
-
known_keys = {
|
|
1485
|
-
|
|
1459
|
+
known_keys = {
|
|
1460
|
+
_decode_attribute_map_key(_FLATTEN.split(desc["key"])[0])
|
|
1461
|
+
for desc in attribute_map.values()
|
|
1462
|
+
if desc["key"] != ""
|
|
1463
|
+
}
|
|
1486
1464
|
present_keys = set(data.keys())
|
|
1487
1465
|
missing_keys = present_keys - known_keys
|
|
1488
1466
|
return {key: data[key] for key in missing_keys}
|
|
@@ -1525,8 +1503,7 @@ class Deserializer(object):
|
|
|
1525
1503
|
return self(target_obj, data, content_type=content_type)
|
|
1526
1504
|
except:
|
|
1527
1505
|
_LOGGER.debug(
|
|
1528
|
-
"Ran into a deserialization error. Ignoring since this is failsafe deserialization",
|
|
1529
|
-
exc_info=True
|
|
1506
|
+
"Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
|
|
1530
1507
|
)
|
|
1531
1508
|
return None
|
|
1532
1509
|
|
|
@@ -1554,22 +1531,16 @@ class Deserializer(object):
|
|
|
1554
1531
|
return context[RawDeserializer.CONTEXT_NAME]
|
|
1555
1532
|
raise ValueError("This pipeline didn't have the RawDeserializer policy; can't deserialize")
|
|
1556
1533
|
|
|
1557
|
-
#Assume this is enough to recognize universal_http.ClientResponse without importing it
|
|
1534
|
+
# Assume this is enough to recognize universal_http.ClientResponse without importing it
|
|
1558
1535
|
if hasattr(raw_data, "body"):
|
|
1559
|
-
return RawDeserializer.deserialize_from_http_generics(
|
|
1560
|
-
raw_data.text(),
|
|
1561
|
-
raw_data.headers
|
|
1562
|
-
)
|
|
1536
|
+
return RawDeserializer.deserialize_from_http_generics(raw_data.text(), raw_data.headers)
|
|
1563
1537
|
|
|
1564
1538
|
# Assume this enough to recognize requests.Response without importing it.
|
|
1565
|
-
if hasattr(raw_data,
|
|
1566
|
-
return RawDeserializer.deserialize_from_http_generics(
|
|
1567
|
-
raw_data.text,
|
|
1568
|
-
raw_data.headers
|
|
1569
|
-
)
|
|
1539
|
+
if hasattr(raw_data, "_content_consumed"):
|
|
1540
|
+
return RawDeserializer.deserialize_from_http_generics(raw_data.text, raw_data.headers)
|
|
1570
1541
|
|
|
1571
|
-
if isinstance(raw_data, (basestring, bytes)) or hasattr(raw_data,
|
|
1572
|
-
return RawDeserializer.deserialize_from_text(raw_data, content_type)
|
|
1542
|
+
if isinstance(raw_data, (basestring, bytes)) or hasattr(raw_data, "read"):
|
|
1543
|
+
return RawDeserializer.deserialize_from_text(raw_data, content_type) # type: ignore
|
|
1573
1544
|
return raw_data
|
|
1574
1545
|
|
|
1575
1546
|
def _instantiate_model(self, response, attrs, additional_properties=None):
|
|
@@ -1579,14 +1550,11 @@ class Deserializer(object):
|
|
|
1579
1550
|
:param d_attrs: The deserialized response attributes.
|
|
1580
1551
|
"""
|
|
1581
1552
|
if callable(response):
|
|
1582
|
-
subtype = getattr(response,
|
|
1553
|
+
subtype = getattr(response, "_subtype_map", {})
|
|
1583
1554
|
try:
|
|
1584
|
-
readonly = [k for k, v in response._validation.items()
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
if v.get('constant')]
|
|
1588
|
-
kwargs = {k: v for k, v in attrs.items()
|
|
1589
|
-
if k not in subtype and k not in readonly + const}
|
|
1555
|
+
readonly = [k for k, v in response._validation.items() if v.get("readonly")]
|
|
1556
|
+
const = [k for k, v in response._validation.items() if v.get("constant")]
|
|
1557
|
+
kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const}
|
|
1590
1558
|
response_obj = response(**kwargs)
|
|
1591
1559
|
for attr in readonly:
|
|
1592
1560
|
setattr(response_obj, attr, attrs.get(attr))
|
|
@@ -1594,8 +1562,7 @@ class Deserializer(object):
|
|
|
1594
1562
|
response_obj.additional_properties = additional_properties
|
|
1595
1563
|
return response_obj
|
|
1596
1564
|
except TypeError as err:
|
|
1597
|
-
msg = "Unable to deserialize {} into model {}. ".format(
|
|
1598
|
-
kwargs, response)
|
|
1565
|
+
msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore
|
|
1599
1566
|
raise DeserializationError(msg + str(err))
|
|
1600
1567
|
else:
|
|
1601
1568
|
try:
|
|
@@ -1659,13 +1626,10 @@ class Deserializer(object):
|
|
|
1659
1626
|
"""
|
|
1660
1627
|
if attr is None:
|
|
1661
1628
|
return None
|
|
1662
|
-
if isinstance(attr, ET.Element):
|
|
1629
|
+
if isinstance(attr, ET.Element): # If I receive an element here, get the children
|
|
1663
1630
|
attr = list(attr)
|
|
1664
1631
|
if not isinstance(attr, (list, set)):
|
|
1665
|
-
raise DeserializationError("Cannot deserialize as [{}] an object of type {}".format(
|
|
1666
|
-
iter_type,
|
|
1667
|
-
type(attr)
|
|
1668
|
-
))
|
|
1632
|
+
raise DeserializationError("Cannot deserialize as [{}] an object of type {}".format(iter_type, type(attr)))
|
|
1669
1633
|
return [self.deserialize_data(a, iter_type) for a in attr]
|
|
1670
1634
|
|
|
1671
1635
|
def deserialize_dict(self, attr, dict_type):
|
|
@@ -1677,7 +1641,7 @@ class Deserializer(object):
|
|
|
1677
1641
|
:rtype: dict
|
|
1678
1642
|
"""
|
|
1679
1643
|
if isinstance(attr, list):
|
|
1680
|
-
return {x[
|
|
1644
|
+
return {x["key"]: self.deserialize_data(x["value"], dict_type) for x in attr}
|
|
1681
1645
|
|
|
1682
1646
|
if isinstance(attr, ET.Element):
|
|
1683
1647
|
# Transform <Key>value</Key> into {"Key": "value"}
|
|
@@ -1698,7 +1662,7 @@ class Deserializer(object):
|
|
|
1698
1662
|
# Do no recurse on XML, just return the tree as-is
|
|
1699
1663
|
return attr
|
|
1700
1664
|
if isinstance(attr, basestring):
|
|
1701
|
-
return self.deserialize_basic(attr,
|
|
1665
|
+
return self.deserialize_basic(attr, "str")
|
|
1702
1666
|
obj_type = type(attr)
|
|
1703
1667
|
if obj_type in self.basic_types:
|
|
1704
1668
|
return self.deserialize_basic(attr, self.basic_types[obj_type])
|
|
@@ -1709,8 +1673,7 @@ class Deserializer(object):
|
|
|
1709
1673
|
deserialized = {}
|
|
1710
1674
|
for key, value in attr.items():
|
|
1711
1675
|
try:
|
|
1712
|
-
deserialized[key] = self.deserialize_object(
|
|
1713
|
-
value, **kwargs)
|
|
1676
|
+
deserialized[key] = self.deserialize_object(value, **kwargs)
|
|
1714
1677
|
except ValueError:
|
|
1715
1678
|
deserialized[key] = None
|
|
1716
1679
|
return deserialized
|
|
@@ -1719,8 +1682,7 @@ class Deserializer(object):
|
|
|
1719
1682
|
deserialized = []
|
|
1720
1683
|
for obj in attr:
|
|
1721
1684
|
try:
|
|
1722
|
-
deserialized.append(self.deserialize_object(
|
|
1723
|
-
obj, **kwargs))
|
|
1685
|
+
deserialized.append(self.deserialize_object(obj, **kwargs))
|
|
1724
1686
|
except ValueError:
|
|
1725
1687
|
pass
|
|
1726
1688
|
return deserialized
|
|
@@ -1747,23 +1709,23 @@ class Deserializer(object):
|
|
|
1747
1709
|
if not attr:
|
|
1748
1710
|
if data_type == "str":
|
|
1749
1711
|
# None or '', node <a/> is empty string.
|
|
1750
|
-
return
|
|
1712
|
+
return ""
|
|
1751
1713
|
else:
|
|
1752
1714
|
# None or '', node <a/> with a strong type is None.
|
|
1753
1715
|
# Don't try to model "empty bool" or "empty int"
|
|
1754
1716
|
return None
|
|
1755
1717
|
|
|
1756
|
-
if data_type ==
|
|
1718
|
+
if data_type == "bool":
|
|
1757
1719
|
if attr in [True, False, 1, 0]:
|
|
1758
1720
|
return bool(attr)
|
|
1759
1721
|
elif isinstance(attr, basestring):
|
|
1760
|
-
if attr.lower() in [
|
|
1722
|
+
if attr.lower() in ["true", "1"]:
|
|
1761
1723
|
return True
|
|
1762
|
-
elif attr.lower() in [
|
|
1724
|
+
elif attr.lower() in ["false", "0"]:
|
|
1763
1725
|
return False
|
|
1764
1726
|
raise TypeError("Invalid boolean value: {}".format(attr))
|
|
1765
1727
|
|
|
1766
|
-
if data_type ==
|
|
1728
|
+
if data_type == "str":
|
|
1767
1729
|
return self.deserialize_unicode(attr)
|
|
1768
1730
|
return eval(data_type)(attr) # nosec
|
|
1769
1731
|
|
|
@@ -1782,7 +1744,7 @@ class Deserializer(object):
|
|
|
1782
1744
|
|
|
1783
1745
|
# Consider this is real string
|
|
1784
1746
|
try:
|
|
1785
|
-
if isinstance(data, unicode):
|
|
1747
|
+
if isinstance(data, unicode): # type: ignore
|
|
1786
1748
|
return data
|
|
1787
1749
|
except NameError:
|
|
1788
1750
|
return str(data)
|
|
@@ -1833,7 +1795,7 @@ class Deserializer(object):
|
|
|
1833
1795
|
"""
|
|
1834
1796
|
if isinstance(attr, ET.Element):
|
|
1835
1797
|
attr = attr.text
|
|
1836
|
-
return bytearray(b64decode(attr))
|
|
1798
|
+
return bytearray(b64decode(attr)) # type: ignore
|
|
1837
1799
|
|
|
1838
1800
|
@staticmethod
|
|
1839
1801
|
def deserialize_base64(attr):
|
|
@@ -1845,9 +1807,9 @@ class Deserializer(object):
|
|
|
1845
1807
|
"""
|
|
1846
1808
|
if isinstance(attr, ET.Element):
|
|
1847
1809
|
attr = attr.text
|
|
1848
|
-
padding =
|
|
1849
|
-
attr = attr + padding
|
|
1850
|
-
encoded = attr.replace(
|
|
1810
|
+
padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore
|
|
1811
|
+
attr = attr + padding # type: ignore
|
|
1812
|
+
encoded = attr.replace("-", "+").replace("_", "/")
|
|
1851
1813
|
return b64decode(encoded)
|
|
1852
1814
|
|
|
1853
1815
|
@staticmethod
|
|
@@ -1861,7 +1823,7 @@ class Deserializer(object):
|
|
|
1861
1823
|
if isinstance(attr, ET.Element):
|
|
1862
1824
|
attr = attr.text
|
|
1863
1825
|
try:
|
|
1864
|
-
return decimal.Decimal(attr)
|
|
1826
|
+
return decimal.Decimal(attr) # type: ignore
|
|
1865
1827
|
except decimal.DecimalException as err:
|
|
1866
1828
|
msg = "Invalid decimal {}".format(attr)
|
|
1867
1829
|
raise_with_traceback(DeserializationError, msg, err)
|
|
@@ -1876,7 +1838,7 @@ class Deserializer(object):
|
|
|
1876
1838
|
"""
|
|
1877
1839
|
if isinstance(attr, ET.Element):
|
|
1878
1840
|
attr = attr.text
|
|
1879
|
-
return _long_type(attr)
|
|
1841
|
+
return _long_type(attr) # type: ignore
|
|
1880
1842
|
|
|
1881
1843
|
@staticmethod
|
|
1882
1844
|
def deserialize_duration(attr):
|
|
@@ -1890,7 +1852,7 @@ class Deserializer(object):
|
|
|
1890
1852
|
attr = attr.text
|
|
1891
1853
|
try:
|
|
1892
1854
|
duration = isodate.parse_duration(attr)
|
|
1893
|
-
except(ValueError, OverflowError, AttributeError) as err:
|
|
1855
|
+
except (ValueError, OverflowError, AttributeError) as err:
|
|
1894
1856
|
msg = "Cannot deserialize duration object."
|
|
1895
1857
|
raise_with_traceback(DeserializationError, msg, err)
|
|
1896
1858
|
else:
|
|
@@ -1906,7 +1868,7 @@ class Deserializer(object):
|
|
|
1906
1868
|
"""
|
|
1907
1869
|
if isinstance(attr, ET.Element):
|
|
1908
1870
|
attr = attr.text
|
|
1909
|
-
if re.search(r"[^\W\d_]", attr, re.I + re.U):
|
|
1871
|
+
if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore
|
|
1910
1872
|
raise DeserializationError("Date must have only digits and -. Received: %s" % attr)
|
|
1911
1873
|
# This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception.
|
|
1912
1874
|
return isodate.parse_date(attr, defaultmonth=None, defaultday=None)
|
|
@@ -1921,7 +1883,7 @@ class Deserializer(object):
|
|
|
1921
1883
|
"""
|
|
1922
1884
|
if isinstance(attr, ET.Element):
|
|
1923
1885
|
attr = attr.text
|
|
1924
|
-
if re.search(r"[^\W\d_]", attr, re.I + re.U):
|
|
1886
|
+
if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore
|
|
1925
1887
|
raise DeserializationError("Date must have only digits and -. Received: %s" % attr)
|
|
1926
1888
|
return isodate.parse_time(attr)
|
|
1927
1889
|
|
|
@@ -1936,10 +1898,9 @@ class Deserializer(object):
|
|
|
1936
1898
|
if isinstance(attr, ET.Element):
|
|
1937
1899
|
attr = attr.text
|
|
1938
1900
|
try:
|
|
1939
|
-
parsed_date = email.utils.parsedate_tz(attr)
|
|
1901
|
+
parsed_date = email.utils.parsedate_tz(attr) # type: ignore
|
|
1940
1902
|
date_obj = datetime.datetime(
|
|
1941
|
-
*parsed_date[:6],
|
|
1942
|
-
tzinfo=_FixedOffset(datetime.timedelta(minutes=(parsed_date[9] or 0)/60))
|
|
1903
|
+
*parsed_date[:6], tzinfo=_FixedOffset(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60))
|
|
1943
1904
|
)
|
|
1944
1905
|
if not date_obj.tzinfo:
|
|
1945
1906
|
date_obj = date_obj.astimezone(tz=TZ_UTC)
|
|
@@ -1960,12 +1921,12 @@ class Deserializer(object):
|
|
|
1960
1921
|
if isinstance(attr, ET.Element):
|
|
1961
1922
|
attr = attr.text
|
|
1962
1923
|
try:
|
|
1963
|
-
attr = attr.upper()
|
|
1924
|
+
attr = attr.upper() # type: ignore
|
|
1964
1925
|
match = Deserializer.valid_date.match(attr)
|
|
1965
1926
|
if not match:
|
|
1966
1927
|
raise ValueError("Invalid datetime string: " + attr)
|
|
1967
1928
|
|
|
1968
|
-
check_decimal = attr.split(
|
|
1929
|
+
check_decimal = attr.split(".")
|
|
1969
1930
|
if len(check_decimal) > 1:
|
|
1970
1931
|
decimal_str = ""
|
|
1971
1932
|
for digit in check_decimal[1]:
|
|
@@ -1980,7 +1941,7 @@ class Deserializer(object):
|
|
|
1980
1941
|
test_utc = date_obj.utctimetuple()
|
|
1981
1942
|
if test_utc.tm_year > 9999 or test_utc.tm_year < 1:
|
|
1982
1943
|
raise OverflowError("Hit max or min date")
|
|
1983
|
-
except(ValueError, OverflowError, AttributeError) as err:
|
|
1944
|
+
except (ValueError, OverflowError, AttributeError) as err:
|
|
1984
1945
|
msg = "Cannot deserialize datetime object."
|
|
1985
1946
|
raise_with_traceback(DeserializationError, msg, err)
|
|
1986
1947
|
else:
|
|
@@ -1996,7 +1957,7 @@ class Deserializer(object):
|
|
|
1996
1957
|
:raises: DeserializationError if format invalid
|
|
1997
1958
|
"""
|
|
1998
1959
|
if isinstance(attr, ET.Element):
|
|
1999
|
-
attr = int(attr.text)
|
|
1960
|
+
attr = int(attr.text) # type: ignore
|
|
2000
1961
|
try:
|
|
2001
1962
|
date_obj = datetime.datetime.fromtimestamp(attr, TZ_UTC)
|
|
2002
1963
|
except ValueError as err:
|