ignos-api-client 2024.5.28.9351__py3-none-any.whl → 20260205.5.1__py3-none-any.whl
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.
- ignos/api/client/__init__.py +9 -3
- ignos/api/client/_client.py +194 -15
- ignos/api/client/_configuration.py +0 -1
- ignos/api/client/_patch.py +7 -6
- ignos/api/client/_utils/__init__.py +6 -0
- ignos/api/client/{_serialization.py → _utils/serialization.py} +324 -281
- ignos/api/client/_utils/utils.py +17 -0
- ignos/api/client/_version.py +1 -1
- ignos/api/client/aio/__init__.py +9 -3
- ignos/api/client/aio/_client.py +194 -15
- ignos/api/client/aio/_configuration.py +0 -1
- ignos/api/client/aio/_patch.py +7 -6
- ignos/api/client/aio/operations/__init__.py +126 -56
- ignos/api/client/aio/operations/_operations.py +26854 -11389
- ignos/api/client/aio/operations/_patch.py +7 -6
- ignos/api/client/models/__init__.py +1148 -534
- ignos/api/client/models/_enums.py +429 -9
- ignos/api/client/models/_models.py +34315 -18073
- ignos/api/client/models/_patch.py +7 -6
- ignos/api/client/operations/__init__.py +126 -56
- ignos/api/client/operations/_operations.py +35952 -16398
- ignos/api/client/operations/_patch.py +7 -6
- ignos_api_client-20260205.5.1.dist-info/METADATA +17 -0
- ignos_api_client-20260205.5.1.dist-info/RECORD +29 -0
- {ignos_api_client-2024.5.28.9351.dist-info → ignos_api_client-20260205.5.1.dist-info}/WHEEL +1 -1
- ignos_api_client-2024.5.28.9351.dist-info/METADATA +0 -12
- ignos_api_client-2024.5.28.9351.dist-info/RECORD +0 -27
- {ignos_api_client-2024.5.28.9351.dist-info → ignos_api_client-20260205.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,30 +1,12 @@
|
|
|
1
|
+
# pylint: disable=line-too-long,useless-suppression,too-many-lines
|
|
2
|
+
# coding=utf-8
|
|
1
3
|
# --------------------------------------------------------------------------
|
|
2
|
-
#
|
|
3
4
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
# of this software and associated documentation files (the ""Software""), to
|
|
9
|
-
# deal in the Software without restriction, including without limitation the
|
|
10
|
-
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
11
|
-
# sell copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
# furnished to do so, subject to the following conditions:
|
|
13
|
-
#
|
|
14
|
-
# The above copyright notice and this permission notice shall be included in
|
|
15
|
-
# all copies or substantial portions of the Software.
|
|
16
|
-
#
|
|
17
|
-
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
22
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
23
|
-
# IN THE SOFTWARE.
|
|
24
|
-
#
|
|
5
|
+
# Licensed under the MIT License. See License.txt in the project root for license information.
|
|
6
|
+
# Code generated by Microsoft (R) AutoRest Code Generator.
|
|
7
|
+
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
25
8
|
# --------------------------------------------------------------------------
|
|
26
9
|
|
|
27
|
-
# pylint: skip-file
|
|
28
10
|
# pyright: reportUnnecessaryTypeIgnoreComment=false
|
|
29
11
|
|
|
30
12
|
from base64 import b64decode, b64encode
|
|
@@ -39,7 +21,6 @@ import re
|
|
|
39
21
|
import sys
|
|
40
22
|
import codecs
|
|
41
23
|
from typing import (
|
|
42
|
-
Dict,
|
|
43
24
|
Any,
|
|
44
25
|
cast,
|
|
45
26
|
Optional,
|
|
@@ -48,11 +29,7 @@ from typing import (
|
|
|
48
29
|
IO,
|
|
49
30
|
Mapping,
|
|
50
31
|
Callable,
|
|
51
|
-
TypeVar,
|
|
52
32
|
MutableMapping,
|
|
53
|
-
Type,
|
|
54
|
-
List,
|
|
55
|
-
Mapping,
|
|
56
33
|
)
|
|
57
34
|
|
|
58
35
|
try:
|
|
@@ -62,13 +39,13 @@ except ImportError:
|
|
|
62
39
|
import xml.etree.ElementTree as ET
|
|
63
40
|
|
|
64
41
|
import isodate # type: ignore
|
|
42
|
+
from typing_extensions import Self
|
|
65
43
|
|
|
66
44
|
from azure.core.exceptions import DeserializationError, SerializationError
|
|
67
45
|
from azure.core.serialization import NULL as CoreNull
|
|
68
46
|
|
|
69
47
|
_BOM = codecs.BOM_UTF8.decode(encoding="utf-8")
|
|
70
48
|
|
|
71
|
-
ModelType = TypeVar("ModelType", bound="Model")
|
|
72
49
|
JSON = MutableMapping[str, Any]
|
|
73
50
|
|
|
74
51
|
|
|
@@ -91,6 +68,8 @@ class RawDeserializer:
|
|
|
91
68
|
:param data: Input, could be bytes or stream (will be decoded with UTF8) or text
|
|
92
69
|
:type data: str or bytes or IO
|
|
93
70
|
:param str content_type: The content type.
|
|
71
|
+
:return: The deserialized data.
|
|
72
|
+
:rtype: object
|
|
94
73
|
"""
|
|
95
74
|
if hasattr(data, "read"):
|
|
96
75
|
# Assume a stream
|
|
@@ -112,7 +91,7 @@ class RawDeserializer:
|
|
|
112
91
|
try:
|
|
113
92
|
return json.loads(data_as_str)
|
|
114
93
|
except ValueError as err:
|
|
115
|
-
raise DeserializationError("JSON is invalid: {}".format(err), err)
|
|
94
|
+
raise DeserializationError("JSON is invalid: {}".format(err), err) from err
|
|
116
95
|
elif "xml" in (content_type or []):
|
|
117
96
|
try:
|
|
118
97
|
|
|
@@ -144,6 +123,8 @@ class RawDeserializer:
|
|
|
144
123
|
# context otherwise.
|
|
145
124
|
_LOGGER.critical("Wasn't XML not JSON, failing")
|
|
146
125
|
raise DeserializationError("XML is invalid") from err
|
|
126
|
+
elif content_type.startswith("text/"):
|
|
127
|
+
return data_as_str
|
|
147
128
|
raise DeserializationError("Cannot deserialize content-type: {}".format(content_type))
|
|
148
129
|
|
|
149
130
|
@classmethod
|
|
@@ -153,6 +134,11 @@ class RawDeserializer:
|
|
|
153
134
|
Use bytes and headers to NOT use any requests/aiohttp or whatever
|
|
154
135
|
specific implementation.
|
|
155
136
|
Headers will tested for "content-type"
|
|
137
|
+
|
|
138
|
+
:param bytes body_bytes: The body of the response.
|
|
139
|
+
:param dict headers: The headers of the response.
|
|
140
|
+
:returns: The deserialized data.
|
|
141
|
+
:rtype: object
|
|
156
142
|
"""
|
|
157
143
|
# Try to use content-type from headers if available
|
|
158
144
|
content_type = None
|
|
@@ -177,80 +163,31 @@ try:
|
|
|
177
163
|
except NameError:
|
|
178
164
|
_long_type = int
|
|
179
165
|
|
|
180
|
-
|
|
181
|
-
class UTC(datetime.tzinfo):
|
|
182
|
-
"""Time Zone info for handling UTC"""
|
|
183
|
-
|
|
184
|
-
def utcoffset(self, dt):
|
|
185
|
-
"""UTF offset for UTC is 0."""
|
|
186
|
-
return datetime.timedelta(0)
|
|
187
|
-
|
|
188
|
-
def tzname(self, dt):
|
|
189
|
-
"""Timestamp representation."""
|
|
190
|
-
return "Z"
|
|
191
|
-
|
|
192
|
-
def dst(self, dt):
|
|
193
|
-
"""No daylight saving for UTC."""
|
|
194
|
-
return datetime.timedelta(hours=1)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
try:
|
|
198
|
-
from datetime import timezone as _FixedOffset # type: ignore
|
|
199
|
-
except ImportError: # Python 2.7
|
|
200
|
-
|
|
201
|
-
class _FixedOffset(datetime.tzinfo): # type: ignore
|
|
202
|
-
"""Fixed offset in minutes east from UTC.
|
|
203
|
-
Copy/pasted from Python doc
|
|
204
|
-
:param datetime.timedelta offset: offset in timedelta format
|
|
205
|
-
"""
|
|
206
|
-
|
|
207
|
-
def __init__(self, offset):
|
|
208
|
-
self.__offset = offset
|
|
209
|
-
|
|
210
|
-
def utcoffset(self, dt):
|
|
211
|
-
return self.__offset
|
|
212
|
-
|
|
213
|
-
def tzname(self, dt):
|
|
214
|
-
return str(self.__offset.total_seconds() / 3600)
|
|
215
|
-
|
|
216
|
-
def __repr__(self):
|
|
217
|
-
return "<FixedOffset {}>".format(self.tzname(None))
|
|
218
|
-
|
|
219
|
-
def dst(self, dt):
|
|
220
|
-
return datetime.timedelta(0)
|
|
221
|
-
|
|
222
|
-
def __getinitargs__(self):
|
|
223
|
-
return (self.__offset,)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
try:
|
|
227
|
-
from datetime import timezone
|
|
228
|
-
|
|
229
|
-
TZ_UTC = timezone.utc
|
|
230
|
-
except ImportError:
|
|
231
|
-
TZ_UTC = UTC() # type: ignore
|
|
166
|
+
TZ_UTC = datetime.timezone.utc
|
|
232
167
|
|
|
233
168
|
_FLATTEN = re.compile(r"(?<!\\)\.")
|
|
234
169
|
|
|
235
170
|
|
|
236
|
-
def attribute_transformer(key, attr_desc, value):
|
|
171
|
+
def attribute_transformer(key, attr_desc, value): # pylint: disable=unused-argument
|
|
237
172
|
"""A key transformer that returns the Python attribute.
|
|
238
173
|
|
|
239
174
|
:param str key: The attribute name
|
|
240
175
|
:param dict attr_desc: The attribute metadata
|
|
241
176
|
:param object value: The value
|
|
242
177
|
:returns: A key using attribute name
|
|
178
|
+
:rtype: str
|
|
243
179
|
"""
|
|
244
180
|
return (key, value)
|
|
245
181
|
|
|
246
182
|
|
|
247
|
-
def full_restapi_key_transformer(key, attr_desc, value):
|
|
183
|
+
def full_restapi_key_transformer(key, attr_desc, value): # pylint: disable=unused-argument
|
|
248
184
|
"""A key transformer that returns the full RestAPI key path.
|
|
249
185
|
|
|
250
|
-
:param str
|
|
186
|
+
:param str key: The attribute name
|
|
251
187
|
:param dict attr_desc: The attribute metadata
|
|
252
188
|
:param object value: The value
|
|
253
189
|
:returns: A list of keys using RestAPI syntax.
|
|
190
|
+
:rtype: list
|
|
254
191
|
"""
|
|
255
192
|
keys = _FLATTEN.split(attr_desc["key"])
|
|
256
193
|
return ([_decode_attribute_map_key(k) for k in keys], value)
|
|
@@ -263,33 +200,40 @@ def last_restapi_key_transformer(key, attr_desc, value):
|
|
|
263
200
|
:param dict attr_desc: The attribute metadata
|
|
264
201
|
:param object value: The value
|
|
265
202
|
:returns: The last RestAPI key.
|
|
203
|
+
:rtype: str
|
|
266
204
|
"""
|
|
267
205
|
key, value = full_restapi_key_transformer(key, attr_desc, value)
|
|
268
206
|
return (key[-1], value)
|
|
269
207
|
|
|
270
208
|
|
|
271
209
|
def _create_xml_node(tag, prefix=None, ns=None):
|
|
272
|
-
"""Create a XML node.
|
|
210
|
+
"""Create a XML node.
|
|
211
|
+
|
|
212
|
+
:param str tag: The tag name
|
|
213
|
+
:param str prefix: The prefix
|
|
214
|
+
:param str ns: The namespace
|
|
215
|
+
:return: The XML node
|
|
216
|
+
:rtype: xml.etree.ElementTree.Element
|
|
217
|
+
"""
|
|
273
218
|
if prefix and ns:
|
|
274
219
|
ET.register_namespace(prefix, ns)
|
|
275
220
|
if ns:
|
|
276
221
|
return ET.Element("{" + ns + "}" + tag)
|
|
277
|
-
|
|
278
|
-
return ET.Element(tag)
|
|
222
|
+
return ET.Element(tag)
|
|
279
223
|
|
|
280
224
|
|
|
281
|
-
class Model
|
|
225
|
+
class Model:
|
|
282
226
|
"""Mixin for all client request body/response body models to support
|
|
283
227
|
serialization and deserialization.
|
|
284
228
|
"""
|
|
285
229
|
|
|
286
|
-
_subtype_map:
|
|
287
|
-
_attribute_map:
|
|
288
|
-
_validation:
|
|
230
|
+
_subtype_map: dict[str, dict[str, Any]] = {}
|
|
231
|
+
_attribute_map: dict[str, dict[str, Any]] = {}
|
|
232
|
+
_validation: dict[str, dict[str, Any]] = {}
|
|
289
233
|
|
|
290
234
|
def __init__(self, **kwargs: Any) -> None:
|
|
291
|
-
self.additional_properties: Optional[
|
|
292
|
-
for k in kwargs:
|
|
235
|
+
self.additional_properties: Optional[dict[str, Any]] = {}
|
|
236
|
+
for k in kwargs: # pylint: disable=consider-using-dict-items
|
|
293
237
|
if k not in self._attribute_map:
|
|
294
238
|
_LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__)
|
|
295
239
|
elif k in self._validation and self._validation[k].get("readonly", False):
|
|
@@ -298,13 +242,23 @@ class Model(object):
|
|
|
298
242
|
setattr(self, k, kwargs[k])
|
|
299
243
|
|
|
300
244
|
def __eq__(self, other: Any) -> bool:
|
|
301
|
-
"""Compare objects by comparing all attributes.
|
|
245
|
+
"""Compare objects by comparing all attributes.
|
|
246
|
+
|
|
247
|
+
:param object other: The object to compare
|
|
248
|
+
:returns: True if objects are equal
|
|
249
|
+
:rtype: bool
|
|
250
|
+
"""
|
|
302
251
|
if isinstance(other, self.__class__):
|
|
303
252
|
return self.__dict__ == other.__dict__
|
|
304
253
|
return False
|
|
305
254
|
|
|
306
255
|
def __ne__(self, other: Any) -> bool:
|
|
307
|
-
"""Compare objects by comparing all attributes.
|
|
256
|
+
"""Compare objects by comparing all attributes.
|
|
257
|
+
|
|
258
|
+
:param object other: The object to compare
|
|
259
|
+
:returns: True if objects are not equal
|
|
260
|
+
:rtype: bool
|
|
261
|
+
"""
|
|
308
262
|
return not self.__eq__(other)
|
|
309
263
|
|
|
310
264
|
def __str__(self) -> str:
|
|
@@ -324,7 +278,11 @@ class Model(object):
|
|
|
324
278
|
|
|
325
279
|
@classmethod
|
|
326
280
|
def _create_xml_node(cls):
|
|
327
|
-
"""Create XML node.
|
|
281
|
+
"""Create XML node.
|
|
282
|
+
|
|
283
|
+
:returns: The XML node
|
|
284
|
+
:rtype: xml.etree.ElementTree.Element
|
|
285
|
+
"""
|
|
328
286
|
try:
|
|
329
287
|
xml_map = cls._xml_map # type: ignore
|
|
330
288
|
except AttributeError:
|
|
@@ -344,12 +302,14 @@ class Model(object):
|
|
|
344
302
|
:rtype: dict
|
|
345
303
|
"""
|
|
346
304
|
serializer = Serializer(self._infer_class_models())
|
|
347
|
-
return serializer._serialize(
|
|
305
|
+
return serializer._serialize( # type: ignore # pylint: disable=protected-access
|
|
306
|
+
self, keep_readonly=keep_readonly, **kwargs
|
|
307
|
+
)
|
|
348
308
|
|
|
349
309
|
def as_dict(
|
|
350
310
|
self,
|
|
351
311
|
keep_readonly: bool = True,
|
|
352
|
-
key_transformer: Callable[[str,
|
|
312
|
+
key_transformer: Callable[[str, dict[str, Any], Any], Any] = attribute_transformer,
|
|
353
313
|
**kwargs: Any
|
|
354
314
|
) -> JSON:
|
|
355
315
|
"""Return a dict that can be serialized using json.dump.
|
|
@@ -378,12 +338,15 @@ class Model(object):
|
|
|
378
338
|
|
|
379
339
|
If you want XML serialization, you can pass the kwargs is_xml=True.
|
|
380
340
|
|
|
341
|
+
:param bool keep_readonly: If you want to serialize the readonly attributes
|
|
381
342
|
:param function key_transformer: A key transformer function.
|
|
382
343
|
:returns: A dict JSON compatible object
|
|
383
344
|
:rtype: dict
|
|
384
345
|
"""
|
|
385
346
|
serializer = Serializer(self._infer_class_models())
|
|
386
|
-
return serializer._serialize(
|
|
347
|
+
return serializer._serialize( # type: ignore # pylint: disable=protected-access
|
|
348
|
+
self, key_transformer=key_transformer, keep_readonly=keep_readonly, **kwargs
|
|
349
|
+
)
|
|
387
350
|
|
|
388
351
|
@classmethod
|
|
389
352
|
def _infer_class_models(cls):
|
|
@@ -393,30 +356,31 @@ class Model(object):
|
|
|
393
356
|
client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)}
|
|
394
357
|
if cls.__name__ not in client_models:
|
|
395
358
|
raise ValueError("Not Autorest generated code")
|
|
396
|
-
except Exception:
|
|
359
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
397
360
|
# Assume it's not Autorest generated (tests?). Add ourselves as dependencies.
|
|
398
361
|
client_models = {cls.__name__: cls}
|
|
399
362
|
return client_models
|
|
400
363
|
|
|
401
364
|
@classmethod
|
|
402
|
-
def deserialize(cls
|
|
365
|
+
def deserialize(cls, data: Any, content_type: Optional[str] = None) -> Self:
|
|
403
366
|
"""Parse a str using the RestAPI syntax and return a model.
|
|
404
367
|
|
|
405
368
|
:param str data: A str using RestAPI structure. JSON by default.
|
|
406
369
|
:param str content_type: JSON by default, set application/xml if XML.
|
|
407
370
|
:returns: An instance of this model
|
|
408
|
-
:raises
|
|
371
|
+
:raises DeserializationError: if something went wrong
|
|
372
|
+
:rtype: Self
|
|
409
373
|
"""
|
|
410
374
|
deserializer = Deserializer(cls._infer_class_models())
|
|
411
375
|
return deserializer(cls.__name__, data, content_type=content_type) # type: ignore
|
|
412
376
|
|
|
413
377
|
@classmethod
|
|
414
378
|
def from_dict(
|
|
415
|
-
cls
|
|
379
|
+
cls,
|
|
416
380
|
data: Any,
|
|
417
|
-
key_extractors: Optional[Callable[[str,
|
|
381
|
+
key_extractors: Optional[Callable[[str, dict[str, Any], Any], Any]] = None,
|
|
418
382
|
content_type: Optional[str] = None,
|
|
419
|
-
) ->
|
|
383
|
+
) -> Self:
|
|
420
384
|
"""Parse a dict using given key extractor return a model.
|
|
421
385
|
|
|
422
386
|
By default consider key
|
|
@@ -424,9 +388,11 @@ class Model(object):
|
|
|
424
388
|
and last_rest_key_case_insensitive_extractor)
|
|
425
389
|
|
|
426
390
|
:param dict data: A dict using RestAPI structure
|
|
391
|
+
:param function key_extractors: A key extractor function.
|
|
427
392
|
:param str content_type: JSON by default, set application/xml if XML.
|
|
428
393
|
:returns: An instance of this model
|
|
429
|
-
:raises
|
|
394
|
+
:raises DeserializationError: if something went wrong
|
|
395
|
+
:rtype: Self
|
|
430
396
|
"""
|
|
431
397
|
deserializer = Deserializer(cls._infer_class_models())
|
|
432
398
|
deserializer.key_extractors = ( # type: ignore
|
|
@@ -446,21 +412,25 @@ class Model(object):
|
|
|
446
412
|
return {}
|
|
447
413
|
result = dict(cls._subtype_map[key])
|
|
448
414
|
for valuetype in cls._subtype_map[key].values():
|
|
449
|
-
result
|
|
415
|
+
result |= objects[valuetype]._flatten_subtype(key, objects) # pylint: disable=protected-access
|
|
450
416
|
return result
|
|
451
417
|
|
|
452
418
|
@classmethod
|
|
453
419
|
def _classify(cls, response, objects):
|
|
454
420
|
"""Check the class _subtype_map for any child classes.
|
|
455
421
|
We want to ignore any inherited _subtype_maps.
|
|
456
|
-
|
|
422
|
+
|
|
423
|
+
:param dict response: The initial data
|
|
424
|
+
:param dict objects: The class objects
|
|
425
|
+
:returns: The class to be used
|
|
426
|
+
:rtype: class
|
|
457
427
|
"""
|
|
458
428
|
for subtype_key in cls.__dict__.get("_subtype_map", {}).keys():
|
|
459
429
|
subtype_value = None
|
|
460
430
|
|
|
461
431
|
if not isinstance(response, ET.Element):
|
|
462
432
|
rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1]
|
|
463
|
-
subtype_value = response.
|
|
433
|
+
subtype_value = response.get(rest_api_response_key, None) or response.get(subtype_key, None)
|
|
464
434
|
else:
|
|
465
435
|
subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response)
|
|
466
436
|
if subtype_value:
|
|
@@ -499,11 +469,13 @@ def _decode_attribute_map_key(key):
|
|
|
499
469
|
inside the received data.
|
|
500
470
|
|
|
501
471
|
:param str key: A key string from the generated code
|
|
472
|
+
:returns: The decoded key
|
|
473
|
+
:rtype: str
|
|
502
474
|
"""
|
|
503
475
|
return key.replace("\\.", ".")
|
|
504
476
|
|
|
505
477
|
|
|
506
|
-
class Serializer
|
|
478
|
+
class Serializer: # pylint: disable=too-many-public-methods
|
|
507
479
|
"""Request object model serializer."""
|
|
508
480
|
|
|
509
481
|
basic_types = {str: "str", int: "int", bool: "bool", float: "float"}
|
|
@@ -538,7 +510,7 @@ class Serializer(object):
|
|
|
538
510
|
"multiple": lambda x, y: x % y != 0,
|
|
539
511
|
}
|
|
540
512
|
|
|
541
|
-
def __init__(self, classes: Optional[Mapping[str, type]] = None):
|
|
513
|
+
def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None:
|
|
542
514
|
self.serialize_type = {
|
|
543
515
|
"iso-8601": Serializer.serialize_iso,
|
|
544
516
|
"rfc-1123": Serializer.serialize_rfc,
|
|
@@ -554,17 +526,20 @@ class Serializer(object):
|
|
|
554
526
|
"[]": self.serialize_iter,
|
|
555
527
|
"{}": self.serialize_dict,
|
|
556
528
|
}
|
|
557
|
-
self.dependencies:
|
|
529
|
+
self.dependencies: dict[str, type] = dict(classes) if classes else {}
|
|
558
530
|
self.key_transformer = full_restapi_key_transformer
|
|
559
531
|
self.client_side_validation = True
|
|
560
532
|
|
|
561
|
-
def _serialize(
|
|
533
|
+
def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, too-many-statements, too-many-locals
|
|
534
|
+
self, target_obj, data_type=None, **kwargs
|
|
535
|
+
):
|
|
562
536
|
"""Serialize data into a string according to type.
|
|
563
537
|
|
|
564
|
-
:param target_obj: The data to be serialized.
|
|
538
|
+
:param object target_obj: The data to be serialized.
|
|
565
539
|
:param str data_type: The type to be serialized from.
|
|
566
540
|
:rtype: str, dict
|
|
567
|
-
:raises
|
|
541
|
+
:raises SerializationError: if serialization fails.
|
|
542
|
+
:returns: The serialized data.
|
|
568
543
|
"""
|
|
569
544
|
key_transformer = kwargs.get("key_transformer", self.key_transformer)
|
|
570
545
|
keep_readonly = kwargs.get("keep_readonly", False)
|
|
@@ -590,17 +565,19 @@ class Serializer(object):
|
|
|
590
565
|
|
|
591
566
|
serialized = {}
|
|
592
567
|
if is_xml_model_serialization:
|
|
593
|
-
serialized = target_obj._create_xml_node()
|
|
568
|
+
serialized = target_obj._create_xml_node() # pylint: disable=protected-access
|
|
594
569
|
try:
|
|
595
|
-
attributes = target_obj._attribute_map
|
|
570
|
+
attributes = target_obj._attribute_map # pylint: disable=protected-access
|
|
596
571
|
for attr, attr_desc in attributes.items():
|
|
597
572
|
attr_name = attr
|
|
598
|
-
if not keep_readonly and target_obj._validation.get(
|
|
573
|
+
if not keep_readonly and target_obj._validation.get( # pylint: disable=protected-access
|
|
574
|
+
attr_name, {}
|
|
575
|
+
).get("readonly", False):
|
|
599
576
|
continue
|
|
600
577
|
|
|
601
578
|
if attr_name == "additional_properties" and attr_desc["key"] == "":
|
|
602
579
|
if target_obj.additional_properties is not None:
|
|
603
|
-
serialized
|
|
580
|
+
serialized |= target_obj.additional_properties
|
|
604
581
|
continue
|
|
605
582
|
try:
|
|
606
583
|
|
|
@@ -631,7 +608,8 @@ class Serializer(object):
|
|
|
631
608
|
if isinstance(new_attr, list):
|
|
632
609
|
serialized.extend(new_attr) # type: ignore
|
|
633
610
|
elif isinstance(new_attr, ET.Element):
|
|
634
|
-
# If the down XML has no XML/Name,
|
|
611
|
+
# If the down XML has no XML/Name,
|
|
612
|
+
# we MUST replace the tag with the local tag. But keeping the namespaces.
|
|
635
613
|
if "name" not in getattr(orig_attr, "_xml_map", {}):
|
|
636
614
|
splitted_tag = new_attr.tag.split("}")
|
|
637
615
|
if len(splitted_tag) == 2: # Namespace
|
|
@@ -662,17 +640,17 @@ class Serializer(object):
|
|
|
662
640
|
except (AttributeError, KeyError, TypeError) as err:
|
|
663
641
|
msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj))
|
|
664
642
|
raise SerializationError(msg) from err
|
|
665
|
-
|
|
666
|
-
return serialized
|
|
643
|
+
return serialized
|
|
667
644
|
|
|
668
645
|
def body(self, data, data_type, **kwargs):
|
|
669
646
|
"""Serialize data intended for a request body.
|
|
670
647
|
|
|
671
|
-
:param data: The data to be serialized.
|
|
648
|
+
:param object data: The data to be serialized.
|
|
672
649
|
:param str data_type: The type to be serialized from.
|
|
673
650
|
:rtype: dict
|
|
674
|
-
:raises
|
|
675
|
-
:raises
|
|
651
|
+
:raises SerializationError: if serialization fails.
|
|
652
|
+
:raises ValueError: if data is None
|
|
653
|
+
:returns: The serialized request body
|
|
676
654
|
"""
|
|
677
655
|
|
|
678
656
|
# Just in case this is a dict
|
|
@@ -701,7 +679,7 @@ class Serializer(object):
|
|
|
701
679
|
attribute_key_case_insensitive_extractor,
|
|
702
680
|
last_rest_key_case_insensitive_extractor,
|
|
703
681
|
]
|
|
704
|
-
data = deserializer._deserialize(data_type, data)
|
|
682
|
+
data = deserializer._deserialize(data_type, data) # pylint: disable=protected-access
|
|
705
683
|
except DeserializationError as err:
|
|
706
684
|
raise SerializationError("Unable to build a model: " + str(err)) from err
|
|
707
685
|
|
|
@@ -710,11 +688,13 @@ class Serializer(object):
|
|
|
710
688
|
def url(self, name, data, data_type, **kwargs):
|
|
711
689
|
"""Serialize data intended for a URL path.
|
|
712
690
|
|
|
713
|
-
:param
|
|
691
|
+
:param str name: The name of the URL path parameter.
|
|
692
|
+
:param object data: The data to be serialized.
|
|
714
693
|
:param str data_type: The type to be serialized from.
|
|
715
694
|
:rtype: str
|
|
716
|
-
:
|
|
717
|
-
:raises:
|
|
695
|
+
:returns: The serialized URL path
|
|
696
|
+
:raises TypeError: if serialization fails.
|
|
697
|
+
:raises ValueError: if data is None
|
|
718
698
|
"""
|
|
719
699
|
try:
|
|
720
700
|
output = self.serialize_data(data, data_type, **kwargs)
|
|
@@ -726,21 +706,20 @@ class Serializer(object):
|
|
|
726
706
|
output = output.replace("{", quote("{")).replace("}", quote("}"))
|
|
727
707
|
else:
|
|
728
708
|
output = quote(str(output), safe="")
|
|
729
|
-
except SerializationError:
|
|
730
|
-
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
731
|
-
|
|
732
|
-
return output
|
|
709
|
+
except SerializationError as exc:
|
|
710
|
+
raise TypeError("{} must be type {}.".format(name, data_type)) from exc
|
|
711
|
+
return output
|
|
733
712
|
|
|
734
713
|
def query(self, name, data, data_type, **kwargs):
|
|
735
714
|
"""Serialize data intended for a URL query.
|
|
736
715
|
|
|
737
|
-
:param
|
|
716
|
+
:param str name: The name of the query parameter.
|
|
717
|
+
:param object data: The data to be serialized.
|
|
738
718
|
:param str data_type: The type to be serialized from.
|
|
739
|
-
:keyword bool skip_quote: Whether to skip quote the serialized result.
|
|
740
|
-
Defaults to False.
|
|
741
719
|
:rtype: str, list
|
|
742
|
-
:raises
|
|
743
|
-
:raises
|
|
720
|
+
:raises TypeError: if serialization fails.
|
|
721
|
+
:raises ValueError: if data is None
|
|
722
|
+
:returns: The serialized query parameter
|
|
744
723
|
"""
|
|
745
724
|
try:
|
|
746
725
|
# Treat the list aside, since we don't want to encode the div separator
|
|
@@ -757,19 +736,20 @@ class Serializer(object):
|
|
|
757
736
|
output = str(output)
|
|
758
737
|
else:
|
|
759
738
|
output = quote(str(output), safe="")
|
|
760
|
-
except SerializationError:
|
|
761
|
-
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
762
|
-
|
|
763
|
-
return str(output)
|
|
739
|
+
except SerializationError as exc:
|
|
740
|
+
raise TypeError("{} must be type {}.".format(name, data_type)) from exc
|
|
741
|
+
return str(output)
|
|
764
742
|
|
|
765
743
|
def header(self, name, data, data_type, **kwargs):
|
|
766
744
|
"""Serialize data intended for a request header.
|
|
767
745
|
|
|
768
|
-
:param
|
|
746
|
+
:param str name: The name of the header.
|
|
747
|
+
:param object data: The data to be serialized.
|
|
769
748
|
:param str data_type: The type to be serialized from.
|
|
770
749
|
:rtype: str
|
|
771
|
-
:raises
|
|
772
|
-
:raises
|
|
750
|
+
:raises TypeError: if serialization fails.
|
|
751
|
+
:raises ValueError: if data is None
|
|
752
|
+
:returns: The serialized header
|
|
773
753
|
"""
|
|
774
754
|
try:
|
|
775
755
|
if data_type in ["[str]"]:
|
|
@@ -778,21 +758,20 @@ class Serializer(object):
|
|
|
778
758
|
output = self.serialize_data(data, data_type, **kwargs)
|
|
779
759
|
if data_type == "bool":
|
|
780
760
|
output = json.dumps(output)
|
|
781
|
-
except SerializationError:
|
|
782
|
-
raise TypeError("{} must be type {}.".format(name, data_type))
|
|
783
|
-
|
|
784
|
-
return str(output)
|
|
761
|
+
except SerializationError as exc:
|
|
762
|
+
raise TypeError("{} must be type {}.".format(name, data_type)) from exc
|
|
763
|
+
return str(output)
|
|
785
764
|
|
|
786
765
|
def serialize_data(self, data, data_type, **kwargs):
|
|
787
766
|
"""Serialize generic data according to supplied data type.
|
|
788
767
|
|
|
789
|
-
:param data: The data to be serialized.
|
|
768
|
+
:param object data: The data to be serialized.
|
|
790
769
|
:param str data_type: The type to be serialized from.
|
|
791
|
-
:
|
|
792
|
-
|
|
793
|
-
:raises:
|
|
794
|
-
:
|
|
795
|
-
:
|
|
770
|
+
:raises AttributeError: if required data is None.
|
|
771
|
+
:raises ValueError: if data is None
|
|
772
|
+
:raises SerializationError: if serialization fails.
|
|
773
|
+
:returns: The serialized data.
|
|
774
|
+
:rtype: str, int, float, bool, dict, list
|
|
796
775
|
"""
|
|
797
776
|
if data is None:
|
|
798
777
|
raise ValueError("No value for given attribute")
|
|
@@ -803,12 +782,12 @@ class Serializer(object):
|
|
|
803
782
|
if data_type in self.basic_types.values():
|
|
804
783
|
return self.serialize_basic(data, data_type, **kwargs)
|
|
805
784
|
|
|
806
|
-
|
|
785
|
+
if data_type in self.serialize_type:
|
|
807
786
|
return self.serialize_type[data_type](data, **kwargs)
|
|
808
787
|
|
|
809
788
|
# If dependencies is empty, try with current data class
|
|
810
789
|
# It has to be a subclass of Enum anyway
|
|
811
|
-
enum_type = self.dependencies.get(data_type, data.__class__)
|
|
790
|
+
enum_type = self.dependencies.get(data_type, cast(type, data.__class__))
|
|
812
791
|
if issubclass(enum_type, Enum):
|
|
813
792
|
return Serializer.serialize_enum(data, enum_obj=enum_type)
|
|
814
793
|
|
|
@@ -819,11 +798,10 @@ class Serializer(object):
|
|
|
819
798
|
except (ValueError, TypeError) as err:
|
|
820
799
|
msg = "Unable to serialize value: {!r} as type: {!r}."
|
|
821
800
|
raise SerializationError(msg.format(data, data_type)) from err
|
|
822
|
-
|
|
823
|
-
return self._serialize(data, **kwargs)
|
|
801
|
+
return self._serialize(data, **kwargs)
|
|
824
802
|
|
|
825
803
|
@classmethod
|
|
826
|
-
def _get_custom_serializers(cls, data_type, **kwargs):
|
|
804
|
+
def _get_custom_serializers(cls, data_type, **kwargs): # pylint: disable=inconsistent-return-statements
|
|
827
805
|
custom_serializer = kwargs.get("basic_types_serializers", {}).get(data_type)
|
|
828
806
|
if custom_serializer:
|
|
829
807
|
return custom_serializer
|
|
@@ -839,23 +817,33 @@ class Serializer(object):
|
|
|
839
817
|
- basic_types_serializers dict[str, callable] : If set, use the callable as serializer
|
|
840
818
|
- is_xml bool : If set, use xml_basic_types_serializers
|
|
841
819
|
|
|
842
|
-
:param data: Object to be serialized.
|
|
820
|
+
:param obj data: Object to be serialized.
|
|
843
821
|
:param str data_type: Type of object in the iterable.
|
|
822
|
+
:rtype: str, int, float, bool
|
|
823
|
+
:return: serialized object
|
|
824
|
+
:raises TypeError: raise if data_type is not one of str, int, float, bool.
|
|
844
825
|
"""
|
|
845
826
|
custom_serializer = cls._get_custom_serializers(data_type, **kwargs)
|
|
846
827
|
if custom_serializer:
|
|
847
828
|
return custom_serializer(data)
|
|
848
829
|
if data_type == "str":
|
|
849
830
|
return cls.serialize_unicode(data)
|
|
850
|
-
|
|
831
|
+
if data_type == "int":
|
|
832
|
+
return int(data)
|
|
833
|
+
if data_type == "float":
|
|
834
|
+
return float(data)
|
|
835
|
+
if data_type == "bool":
|
|
836
|
+
return bool(data)
|
|
837
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
851
838
|
|
|
852
839
|
@classmethod
|
|
853
840
|
def serialize_unicode(cls, data):
|
|
854
841
|
"""Special handling for serializing unicode strings in Py2.
|
|
855
842
|
Encode to UTF-8 if unicode, otherwise handle as a str.
|
|
856
843
|
|
|
857
|
-
:param data: Object to be serialized.
|
|
844
|
+
:param str data: Object to be serialized.
|
|
858
845
|
:rtype: str
|
|
846
|
+
:return: serialized object
|
|
859
847
|
"""
|
|
860
848
|
try: # If I received an enum, return its value
|
|
861
849
|
return data.value
|
|
@@ -869,8 +857,7 @@ class Serializer(object):
|
|
|
869
857
|
return data
|
|
870
858
|
except NameError:
|
|
871
859
|
return str(data)
|
|
872
|
-
|
|
873
|
-
return str(data)
|
|
860
|
+
return str(data)
|
|
874
861
|
|
|
875
862
|
def serialize_iter(self, data, iter_type, div=None, **kwargs):
|
|
876
863
|
"""Serialize iterable.
|
|
@@ -880,15 +867,13 @@ class Serializer(object):
|
|
|
880
867
|
serialization_ctxt['type'] should be same as data_type.
|
|
881
868
|
- is_xml bool : If set, serialize as XML
|
|
882
869
|
|
|
883
|
-
:param list
|
|
870
|
+
:param list data: Object to be serialized.
|
|
884
871
|
:param str iter_type: Type of object in the iterable.
|
|
885
|
-
:param bool required: Whether the objects in the iterable must
|
|
886
|
-
not be None or empty.
|
|
887
872
|
:param str div: If set, this str will be used to combine the elements
|
|
888
873
|
in the iterable into a combined string. Default is 'None'.
|
|
889
|
-
:keyword bool do_quote: Whether to quote the serialized result of each iterable element.
|
|
890
874
|
Defaults to False.
|
|
891
875
|
:rtype: list, str
|
|
876
|
+
:return: serialized iterable
|
|
892
877
|
"""
|
|
893
878
|
if isinstance(data, str):
|
|
894
879
|
raise SerializationError("Refuse str type as a valid iter type.")
|
|
@@ -943,9 +928,8 @@ class Serializer(object):
|
|
|
943
928
|
|
|
944
929
|
:param dict attr: Object to be serialized.
|
|
945
930
|
:param str dict_type: Type of object in the dictionary.
|
|
946
|
-
:param bool required: Whether the objects in the dictionary must
|
|
947
|
-
not be None or empty.
|
|
948
931
|
:rtype: dict
|
|
932
|
+
:return: serialized dictionary
|
|
949
933
|
"""
|
|
950
934
|
serialization_ctxt = kwargs.get("serialization_ctxt", {})
|
|
951
935
|
serialized = {}
|
|
@@ -969,7 +953,7 @@ class Serializer(object):
|
|
|
969
953
|
|
|
970
954
|
return serialized
|
|
971
955
|
|
|
972
|
-
def serialize_object(self, attr, **kwargs):
|
|
956
|
+
def serialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements
|
|
973
957
|
"""Serialize a generic object.
|
|
974
958
|
This will be handled as a dictionary. If object passed in is not
|
|
975
959
|
a basic type (str, int, float, dict, list) it will simply be
|
|
@@ -977,6 +961,7 @@ class Serializer(object):
|
|
|
977
961
|
|
|
978
962
|
:param dict attr: Object to be serialized.
|
|
979
963
|
:rtype: dict or str
|
|
964
|
+
:return: serialized object
|
|
980
965
|
"""
|
|
981
966
|
if attr is None:
|
|
982
967
|
return None
|
|
@@ -1001,7 +986,7 @@ class Serializer(object):
|
|
|
1001
986
|
return self.serialize_decimal(attr)
|
|
1002
987
|
|
|
1003
988
|
# If it's a model or I know this dependency, serialize as a Model
|
|
1004
|
-
|
|
989
|
+
if obj_type in self.dependencies.values() or isinstance(attr, Model):
|
|
1005
990
|
return self._serialize(attr)
|
|
1006
991
|
|
|
1007
992
|
if obj_type == dict:
|
|
@@ -1032,56 +1017,61 @@ class Serializer(object):
|
|
|
1032
1017
|
try:
|
|
1033
1018
|
enum_obj(result) # type: ignore
|
|
1034
1019
|
return result
|
|
1035
|
-
except ValueError:
|
|
1020
|
+
except ValueError as exc:
|
|
1036
1021
|
for enum_value in enum_obj: # type: ignore
|
|
1037
1022
|
if enum_value.value.lower() == str(attr).lower():
|
|
1038
1023
|
return enum_value.value
|
|
1039
1024
|
error = "{!r} is not valid value for enum {!r}"
|
|
1040
|
-
raise SerializationError(error.format(attr, enum_obj))
|
|
1025
|
+
raise SerializationError(error.format(attr, enum_obj)) from exc
|
|
1041
1026
|
|
|
1042
1027
|
@staticmethod
|
|
1043
|
-
def serialize_bytearray(attr, **kwargs):
|
|
1028
|
+
def serialize_bytearray(attr, **kwargs): # pylint: disable=unused-argument
|
|
1044
1029
|
"""Serialize bytearray into base-64 string.
|
|
1045
1030
|
|
|
1046
|
-
:param attr: Object to be serialized.
|
|
1031
|
+
:param str attr: Object to be serialized.
|
|
1047
1032
|
:rtype: str
|
|
1033
|
+
:return: serialized base64
|
|
1048
1034
|
"""
|
|
1049
1035
|
return b64encode(attr).decode()
|
|
1050
1036
|
|
|
1051
1037
|
@staticmethod
|
|
1052
|
-
def serialize_base64(attr, **kwargs):
|
|
1038
|
+
def serialize_base64(attr, **kwargs): # pylint: disable=unused-argument
|
|
1053
1039
|
"""Serialize str into base-64 string.
|
|
1054
1040
|
|
|
1055
|
-
:param attr: Object to be serialized.
|
|
1041
|
+
:param str attr: Object to be serialized.
|
|
1056
1042
|
:rtype: str
|
|
1043
|
+
:return: serialized base64
|
|
1057
1044
|
"""
|
|
1058
1045
|
encoded = b64encode(attr).decode("ascii")
|
|
1059
1046
|
return encoded.strip("=").replace("+", "-").replace("/", "_")
|
|
1060
1047
|
|
|
1061
1048
|
@staticmethod
|
|
1062
|
-
def serialize_decimal(attr, **kwargs):
|
|
1049
|
+
def serialize_decimal(attr, **kwargs): # pylint: disable=unused-argument
|
|
1063
1050
|
"""Serialize Decimal object to float.
|
|
1064
1051
|
|
|
1065
|
-
:param attr: Object to be serialized.
|
|
1052
|
+
:param decimal attr: Object to be serialized.
|
|
1066
1053
|
:rtype: float
|
|
1054
|
+
:return: serialized decimal
|
|
1067
1055
|
"""
|
|
1068
1056
|
return float(attr)
|
|
1069
1057
|
|
|
1070
1058
|
@staticmethod
|
|
1071
|
-
def serialize_long(attr, **kwargs):
|
|
1059
|
+
def serialize_long(attr, **kwargs): # pylint: disable=unused-argument
|
|
1072
1060
|
"""Serialize long (Py2) or int (Py3).
|
|
1073
1061
|
|
|
1074
|
-
:param attr: Object to be serialized.
|
|
1062
|
+
:param int attr: Object to be serialized.
|
|
1075
1063
|
:rtype: int/long
|
|
1064
|
+
:return: serialized long
|
|
1076
1065
|
"""
|
|
1077
1066
|
return _long_type(attr)
|
|
1078
1067
|
|
|
1079
1068
|
@staticmethod
|
|
1080
|
-
def serialize_date(attr, **kwargs):
|
|
1069
|
+
def serialize_date(attr, **kwargs): # pylint: disable=unused-argument
|
|
1081
1070
|
"""Serialize Date object into ISO-8601 formatted string.
|
|
1082
1071
|
|
|
1083
1072
|
:param Date attr: Object to be serialized.
|
|
1084
1073
|
:rtype: str
|
|
1074
|
+
:return: serialized date
|
|
1085
1075
|
"""
|
|
1086
1076
|
if isinstance(attr, str):
|
|
1087
1077
|
attr = isodate.parse_date(attr)
|
|
@@ -1089,11 +1079,12 @@ class Serializer(object):
|
|
|
1089
1079
|
return t
|
|
1090
1080
|
|
|
1091
1081
|
@staticmethod
|
|
1092
|
-
def serialize_time(attr, **kwargs):
|
|
1082
|
+
def serialize_time(attr, **kwargs): # pylint: disable=unused-argument
|
|
1093
1083
|
"""Serialize Time object into ISO-8601 formatted string.
|
|
1094
1084
|
|
|
1095
1085
|
:param datetime.time attr: Object to be serialized.
|
|
1096
1086
|
:rtype: str
|
|
1087
|
+
:return: serialized time
|
|
1097
1088
|
"""
|
|
1098
1089
|
if isinstance(attr, str):
|
|
1099
1090
|
attr = isodate.parse_time(attr)
|
|
@@ -1103,30 +1094,32 @@ class Serializer(object):
|
|
|
1103
1094
|
return t
|
|
1104
1095
|
|
|
1105
1096
|
@staticmethod
|
|
1106
|
-
def serialize_duration(attr, **kwargs):
|
|
1097
|
+
def serialize_duration(attr, **kwargs): # pylint: disable=unused-argument
|
|
1107
1098
|
"""Serialize TimeDelta object into ISO-8601 formatted string.
|
|
1108
1099
|
|
|
1109
1100
|
:param TimeDelta attr: Object to be serialized.
|
|
1110
1101
|
:rtype: str
|
|
1102
|
+
:return: serialized duration
|
|
1111
1103
|
"""
|
|
1112
1104
|
if isinstance(attr, str):
|
|
1113
1105
|
attr = isodate.parse_duration(attr)
|
|
1114
1106
|
return isodate.duration_isoformat(attr)
|
|
1115
1107
|
|
|
1116
1108
|
@staticmethod
|
|
1117
|
-
def serialize_rfc(attr, **kwargs):
|
|
1109
|
+
def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument
|
|
1118
1110
|
"""Serialize Datetime object into RFC-1123 formatted string.
|
|
1119
1111
|
|
|
1120
1112
|
:param Datetime attr: Object to be serialized.
|
|
1121
1113
|
:rtype: str
|
|
1122
|
-
:raises
|
|
1114
|
+
:raises TypeError: if format invalid.
|
|
1115
|
+
:return: serialized rfc
|
|
1123
1116
|
"""
|
|
1124
1117
|
try:
|
|
1125
1118
|
if not attr.tzinfo:
|
|
1126
1119
|
_LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
|
|
1127
1120
|
utc = attr.utctimetuple()
|
|
1128
|
-
except AttributeError:
|
|
1129
|
-
raise TypeError("RFC1123 object must be valid Datetime object.")
|
|
1121
|
+
except AttributeError as exc:
|
|
1122
|
+
raise TypeError("RFC1123 object must be valid Datetime object.") from exc
|
|
1130
1123
|
|
|
1131
1124
|
return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format(
|
|
1132
1125
|
Serializer.days[utc.tm_wday],
|
|
@@ -1139,12 +1132,13 @@ class Serializer(object):
|
|
|
1139
1132
|
)
|
|
1140
1133
|
|
|
1141
1134
|
@staticmethod
|
|
1142
|
-
def serialize_iso(attr, **kwargs):
|
|
1135
|
+
def serialize_iso(attr, **kwargs): # pylint: disable=unused-argument
|
|
1143
1136
|
"""Serialize Datetime object into ISO-8601 formatted string.
|
|
1144
1137
|
|
|
1145
1138
|
:param Datetime attr: Object to be serialized.
|
|
1146
1139
|
:rtype: str
|
|
1147
|
-
:raises
|
|
1140
|
+
:raises SerializationError: if format invalid.
|
|
1141
|
+
:return: serialized iso
|
|
1148
1142
|
"""
|
|
1149
1143
|
if isinstance(attr, str):
|
|
1150
1144
|
attr = isodate.parse_datetime(attr)
|
|
@@ -1170,13 +1164,14 @@ class Serializer(object):
|
|
|
1170
1164
|
raise TypeError(msg) from err
|
|
1171
1165
|
|
|
1172
1166
|
@staticmethod
|
|
1173
|
-
def serialize_unix(attr, **kwargs):
|
|
1167
|
+
def serialize_unix(attr, **kwargs): # pylint: disable=unused-argument
|
|
1174
1168
|
"""Serialize Datetime object into IntTime format.
|
|
1175
1169
|
This is represented as seconds.
|
|
1176
1170
|
|
|
1177
1171
|
:param Datetime attr: Object to be serialized.
|
|
1178
1172
|
:rtype: int
|
|
1179
|
-
:raises
|
|
1173
|
+
:raises SerializationError: if format invalid
|
|
1174
|
+
:return: serialied unix
|
|
1180
1175
|
"""
|
|
1181
1176
|
if isinstance(attr, int):
|
|
1182
1177
|
return attr
|
|
@@ -1184,17 +1179,17 @@ class Serializer(object):
|
|
|
1184
1179
|
if not attr.tzinfo:
|
|
1185
1180
|
_LOGGER.warning("Datetime with no tzinfo will be considered UTC.")
|
|
1186
1181
|
return int(calendar.timegm(attr.utctimetuple()))
|
|
1187
|
-
except AttributeError:
|
|
1188
|
-
raise TypeError("Unix time object must be valid Datetime object.")
|
|
1182
|
+
except AttributeError as exc:
|
|
1183
|
+
raise TypeError("Unix time object must be valid Datetime object.") from exc
|
|
1189
1184
|
|
|
1190
1185
|
|
|
1191
|
-
def rest_key_extractor(attr, attr_desc, data):
|
|
1186
|
+
def rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument
|
|
1192
1187
|
key = attr_desc["key"]
|
|
1193
1188
|
working_data = data
|
|
1194
1189
|
|
|
1195
1190
|
while "." in key:
|
|
1196
1191
|
# Need the cast, as for some reasons "split" is typed as list[str | Any]
|
|
1197
|
-
dict_keys = cast(
|
|
1192
|
+
dict_keys = cast(list[str], _FLATTEN.split(key))
|
|
1198
1193
|
if len(dict_keys) == 1:
|
|
1199
1194
|
key = _decode_attribute_map_key(dict_keys[0])
|
|
1200
1195
|
break
|
|
@@ -1209,7 +1204,9 @@ def rest_key_extractor(attr, attr_desc, data):
|
|
|
1209
1204
|
return working_data.get(key)
|
|
1210
1205
|
|
|
1211
1206
|
|
|
1212
|
-
def rest_key_case_insensitive_extractor(
|
|
1207
|
+
def rest_key_case_insensitive_extractor( # pylint: disable=unused-argument, inconsistent-return-statements
|
|
1208
|
+
attr, attr_desc, data
|
|
1209
|
+
):
|
|
1213
1210
|
key = attr_desc["key"]
|
|
1214
1211
|
working_data = data
|
|
1215
1212
|
|
|
@@ -1230,17 +1227,29 @@ def rest_key_case_insensitive_extractor(attr, attr_desc, data):
|
|
|
1230
1227
|
return attribute_key_case_insensitive_extractor(key, None, working_data)
|
|
1231
1228
|
|
|
1232
1229
|
|
|
1233
|
-
def last_rest_key_extractor(attr, attr_desc, data):
|
|
1234
|
-
"""Extract the attribute in "data" based on the last part of the JSON path key.
|
|
1230
|
+
def last_rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument
|
|
1231
|
+
"""Extract the attribute in "data" based on the last part of the JSON path key.
|
|
1232
|
+
|
|
1233
|
+
:param str attr: The attribute to extract
|
|
1234
|
+
:param dict attr_desc: The attribute description
|
|
1235
|
+
:param dict data: The data to extract from
|
|
1236
|
+
:rtype: object
|
|
1237
|
+
:returns: The extracted attribute
|
|
1238
|
+
"""
|
|
1235
1239
|
key = attr_desc["key"]
|
|
1236
1240
|
dict_keys = _FLATTEN.split(key)
|
|
1237
1241
|
return attribute_key_extractor(dict_keys[-1], None, data)
|
|
1238
1242
|
|
|
1239
1243
|
|
|
1240
|
-
def last_rest_key_case_insensitive_extractor(attr, attr_desc, data):
|
|
1244
|
+
def last_rest_key_case_insensitive_extractor(attr, attr_desc, data): # pylint: disable=unused-argument
|
|
1241
1245
|
"""Extract the attribute in "data" based on the last part of the JSON path key.
|
|
1242
1246
|
|
|
1243
1247
|
This is the case insensitive version of "last_rest_key_extractor"
|
|
1248
|
+
:param str attr: The attribute to extract
|
|
1249
|
+
:param dict attr_desc: The attribute description
|
|
1250
|
+
:param dict data: The data to extract from
|
|
1251
|
+
:rtype: object
|
|
1252
|
+
:returns: The extracted attribute
|
|
1244
1253
|
"""
|
|
1245
1254
|
key = attr_desc["key"]
|
|
1246
1255
|
dict_keys = _FLATTEN.split(key)
|
|
@@ -1277,7 +1286,7 @@ def _extract_name_from_internal_type(internal_type):
|
|
|
1277
1286
|
return xml_name
|
|
1278
1287
|
|
|
1279
1288
|
|
|
1280
|
-
def xml_key_extractor(attr, attr_desc, data):
|
|
1289
|
+
def xml_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument,too-many-return-statements
|
|
1281
1290
|
if isinstance(data, dict):
|
|
1282
1291
|
return None
|
|
1283
1292
|
|
|
@@ -1329,22 +1338,21 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1329
1338
|
if is_iter_type:
|
|
1330
1339
|
if is_wrapped:
|
|
1331
1340
|
return None # is_wrapped no node, we want None
|
|
1332
|
-
|
|
1333
|
-
return [] # not wrapped, assume empty list
|
|
1341
|
+
return [] # not wrapped, assume empty list
|
|
1334
1342
|
return None # Assume it's not there, maybe an optional node.
|
|
1335
1343
|
|
|
1336
1344
|
# If is_iter_type and not wrapped, return all found children
|
|
1337
1345
|
if is_iter_type:
|
|
1338
1346
|
if not is_wrapped:
|
|
1339
1347
|
return children
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
)
|
|
1348
|
+
# Iter and wrapped, should have found one node only (the wrap one)
|
|
1349
|
+
if len(children) != 1:
|
|
1350
|
+
raise DeserializationError(
|
|
1351
|
+
"Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format(
|
|
1352
|
+
xml_name
|
|
1346
1353
|
)
|
|
1347
|
-
|
|
1354
|
+
)
|
|
1355
|
+
return list(children[0]) # Might be empty list and that's ok.
|
|
1348
1356
|
|
|
1349
1357
|
# Here it's not a itertype, we should have found one element only or empty
|
|
1350
1358
|
if len(children) > 1:
|
|
@@ -1352,7 +1360,7 @@ def xml_key_extractor(attr, attr_desc, data):
|
|
|
1352
1360
|
return children[0]
|
|
1353
1361
|
|
|
1354
1362
|
|
|
1355
|
-
class Deserializer
|
|
1363
|
+
class Deserializer:
|
|
1356
1364
|
"""Response object model deserializer.
|
|
1357
1365
|
|
|
1358
1366
|
:param dict classes: Class type dictionary for deserializing complex types.
|
|
@@ -1361,9 +1369,9 @@ class Deserializer(object):
|
|
|
1361
1369
|
|
|
1362
1370
|
basic_types = {str: "str", int: "int", bool: "bool", float: "float"}
|
|
1363
1371
|
|
|
1364
|
-
valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}
|
|
1372
|
+
valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
|
|
1365
1373
|
|
|
1366
|
-
def __init__(self, classes: Optional[Mapping[str, type]] = None):
|
|
1374
|
+
def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None:
|
|
1367
1375
|
self.deserialize_type = {
|
|
1368
1376
|
"iso-8601": Deserializer.deserialize_iso,
|
|
1369
1377
|
"rfc-1123": Deserializer.deserialize_rfc,
|
|
@@ -1383,7 +1391,7 @@ class Deserializer(object):
|
|
|
1383
1391
|
"duration": (isodate.Duration, datetime.timedelta),
|
|
1384
1392
|
"iso-8601": (datetime.datetime),
|
|
1385
1393
|
}
|
|
1386
|
-
self.dependencies:
|
|
1394
|
+
self.dependencies: dict[str, type] = dict(classes) if classes else {}
|
|
1387
1395
|
self.key_extractors = [rest_key_extractor, xml_key_extractor]
|
|
1388
1396
|
# Additional properties only works if the "rest_key_extractor" is used to
|
|
1389
1397
|
# extract the keys. Making it to work whatever the key extractor is too much
|
|
@@ -1399,27 +1407,29 @@ class Deserializer(object):
|
|
|
1399
1407
|
:param str target_obj: Target data type to deserialize to.
|
|
1400
1408
|
:param requests.Response response_data: REST response object.
|
|
1401
1409
|
:param str content_type: Swagger "produces" if available.
|
|
1402
|
-
:raises
|
|
1410
|
+
:raises DeserializationError: if deserialization fails.
|
|
1403
1411
|
:return: Deserialized object.
|
|
1412
|
+
:rtype: object
|
|
1404
1413
|
"""
|
|
1405
1414
|
data = self._unpack_content(response_data, content_type)
|
|
1406
1415
|
return self._deserialize(target_obj, data)
|
|
1407
1416
|
|
|
1408
|
-
def _deserialize(self, target_obj, data):
|
|
1417
|
+
def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return-statements
|
|
1409
1418
|
"""Call the deserializer on a model.
|
|
1410
1419
|
|
|
1411
1420
|
Data needs to be already deserialized as JSON or XML ElementTree
|
|
1412
1421
|
|
|
1413
1422
|
:param str target_obj: Target data type to deserialize to.
|
|
1414
1423
|
:param object data: Object to deserialize.
|
|
1415
|
-
:raises
|
|
1424
|
+
:raises DeserializationError: if deserialization fails.
|
|
1416
1425
|
:return: Deserialized object.
|
|
1426
|
+
:rtype: object
|
|
1417
1427
|
"""
|
|
1418
1428
|
# This is already a model, go recursive just in case
|
|
1419
1429
|
if hasattr(data, "_attribute_map"):
|
|
1420
1430
|
constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")]
|
|
1421
1431
|
try:
|
|
1422
|
-
for attr, mapconfig in data._attribute_map.items():
|
|
1432
|
+
for attr, mapconfig in data._attribute_map.items(): # pylint: disable=protected-access
|
|
1423
1433
|
if attr in constants:
|
|
1424
1434
|
continue
|
|
1425
1435
|
value = getattr(data, attr)
|
|
@@ -1438,13 +1448,13 @@ class Deserializer(object):
|
|
|
1438
1448
|
|
|
1439
1449
|
if isinstance(response, str):
|
|
1440
1450
|
return self.deserialize_data(data, response)
|
|
1441
|
-
|
|
1451
|
+
if isinstance(response, type) and issubclass(response, Enum):
|
|
1442
1452
|
return self.deserialize_enum(data, response)
|
|
1443
1453
|
|
|
1444
|
-
if data is None:
|
|
1454
|
+
if data is None or data is CoreNull:
|
|
1445
1455
|
return data
|
|
1446
1456
|
try:
|
|
1447
|
-
attributes = response._attribute_map # type: ignore
|
|
1457
|
+
attributes = response._attribute_map # type: ignore # pylint: disable=protected-access
|
|
1448
1458
|
d_attrs = {}
|
|
1449
1459
|
for attr, attr_desc in attributes.items():
|
|
1450
1460
|
# Check empty string. If it's not empty, someone has a real "additionalProperties"...
|
|
@@ -1474,9 +1484,8 @@ class Deserializer(object):
|
|
|
1474
1484
|
except (AttributeError, TypeError, KeyError) as err:
|
|
1475
1485
|
msg = "Unable to deserialize to object: " + class_name # type: ignore
|
|
1476
1486
|
raise DeserializationError(msg) from err
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
return self._instantiate_model(response, d_attrs, additional_properties)
|
|
1487
|
+
additional_properties = self._build_additional_properties(attributes, data)
|
|
1488
|
+
return self._instantiate_model(response, d_attrs, additional_properties)
|
|
1480
1489
|
|
|
1481
1490
|
def _build_additional_properties(self, attribute_map, data):
|
|
1482
1491
|
if not self.additional_properties_detection:
|
|
@@ -1503,6 +1512,8 @@ class Deserializer(object):
|
|
|
1503
1512
|
|
|
1504
1513
|
:param str target: The target object type to deserialize to.
|
|
1505
1514
|
:param str/dict data: The response data to deserialize.
|
|
1515
|
+
:return: The classified target object and its class name.
|
|
1516
|
+
:rtype: tuple
|
|
1506
1517
|
"""
|
|
1507
1518
|
if target is None:
|
|
1508
1519
|
return None, None
|
|
@@ -1514,7 +1525,7 @@ class Deserializer(object):
|
|
|
1514
1525
|
return target, target
|
|
1515
1526
|
|
|
1516
1527
|
try:
|
|
1517
|
-
target = target._classify(data, self.dependencies) # type: ignore
|
|
1528
|
+
target = target._classify(data, self.dependencies) # type: ignore # pylint: disable=protected-access
|
|
1518
1529
|
except AttributeError:
|
|
1519
1530
|
pass # Target is not a Model, no classify
|
|
1520
1531
|
return target, target.__class__.__name__ # type: ignore
|
|
@@ -1529,10 +1540,12 @@ class Deserializer(object):
|
|
|
1529
1540
|
:param str target_obj: The target object type to deserialize to.
|
|
1530
1541
|
:param str/dict data: The response data to deserialize.
|
|
1531
1542
|
:param str content_type: Swagger "produces" if available.
|
|
1543
|
+
:return: Deserialized object.
|
|
1544
|
+
:rtype: object
|
|
1532
1545
|
"""
|
|
1533
1546
|
try:
|
|
1534
1547
|
return self(target_obj, data, content_type=content_type)
|
|
1535
|
-
except:
|
|
1548
|
+
except: # pylint: disable=bare-except
|
|
1536
1549
|
_LOGGER.debug(
|
|
1537
1550
|
"Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
|
|
1538
1551
|
)
|
|
@@ -1550,10 +1563,12 @@ class Deserializer(object):
|
|
|
1550
1563
|
|
|
1551
1564
|
If raw_data is something else, bypass all logic and return it directly.
|
|
1552
1565
|
|
|
1553
|
-
:param raw_data: Data to be processed.
|
|
1554
|
-
:param content_type: How to parse if raw_data is a string/bytes.
|
|
1566
|
+
:param obj raw_data: Data to be processed.
|
|
1567
|
+
:param str content_type: How to parse if raw_data is a string/bytes.
|
|
1555
1568
|
:raises JSONDecodeError: If JSON is requested and parsing is impossible.
|
|
1556
1569
|
:raises UnicodeDecodeError: If bytes is not UTF8
|
|
1570
|
+
:rtype: object
|
|
1571
|
+
:return: Unpacked content.
|
|
1557
1572
|
"""
|
|
1558
1573
|
# Assume this is enough to detect a Pipeline Response without importing it
|
|
1559
1574
|
context = getattr(raw_data, "context", {})
|
|
@@ -1577,24 +1592,35 @@ class Deserializer(object):
|
|
|
1577
1592
|
def _instantiate_model(self, response, attrs, additional_properties=None):
|
|
1578
1593
|
"""Instantiate a response model passing in deserialized args.
|
|
1579
1594
|
|
|
1580
|
-
:param response: The response model class.
|
|
1581
|
-
:param
|
|
1595
|
+
:param Response response: The response model class.
|
|
1596
|
+
:param dict attrs: The deserialized response attributes.
|
|
1597
|
+
:param dict additional_properties: Additional properties to be set.
|
|
1598
|
+
:rtype: Response
|
|
1599
|
+
:return: The instantiated response model.
|
|
1582
1600
|
"""
|
|
1583
1601
|
if callable(response):
|
|
1584
1602
|
subtype = getattr(response, "_subtype_map", {})
|
|
1585
1603
|
try:
|
|
1586
|
-
readonly = [
|
|
1587
|
-
|
|
1604
|
+
readonly = [
|
|
1605
|
+
k
|
|
1606
|
+
for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore
|
|
1607
|
+
if v.get("readonly")
|
|
1608
|
+
]
|
|
1609
|
+
const = [
|
|
1610
|
+
k
|
|
1611
|
+
for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore
|
|
1612
|
+
if v.get("constant")
|
|
1613
|
+
]
|
|
1588
1614
|
kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const}
|
|
1589
1615
|
response_obj = response(**kwargs)
|
|
1590
1616
|
for attr in readonly:
|
|
1591
1617
|
setattr(response_obj, attr, attrs.get(attr))
|
|
1592
1618
|
if additional_properties:
|
|
1593
|
-
response_obj.additional_properties = additional_properties
|
|
1619
|
+
response_obj.additional_properties = additional_properties # type: ignore
|
|
1594
1620
|
return response_obj
|
|
1595
1621
|
except TypeError as err:
|
|
1596
1622
|
msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore
|
|
1597
|
-
raise DeserializationError(msg + str(err))
|
|
1623
|
+
raise DeserializationError(msg + str(err)) from err
|
|
1598
1624
|
else:
|
|
1599
1625
|
try:
|
|
1600
1626
|
for attr, value in attrs.items():
|
|
@@ -1603,15 +1629,16 @@ class Deserializer(object):
|
|
|
1603
1629
|
except Exception as exp:
|
|
1604
1630
|
msg = "Unable to populate response model. "
|
|
1605
1631
|
msg += "Type: {}, Error: {}".format(type(response), exp)
|
|
1606
|
-
raise DeserializationError(msg)
|
|
1632
|
+
raise DeserializationError(msg) from exp
|
|
1607
1633
|
|
|
1608
|
-
def deserialize_data(self, data, data_type):
|
|
1634
|
+
def deserialize_data(self, data, data_type): # pylint: disable=too-many-return-statements
|
|
1609
1635
|
"""Process data for deserialization according to data type.
|
|
1610
1636
|
|
|
1611
1637
|
:param str data: The response string to be deserialized.
|
|
1612
1638
|
:param str data_type: The type to deserialize to.
|
|
1613
|
-
:raises
|
|
1639
|
+
:raises DeserializationError: if deserialization fails.
|
|
1614
1640
|
:return: Deserialized object.
|
|
1641
|
+
:rtype: object
|
|
1615
1642
|
"""
|
|
1616
1643
|
if data is None:
|
|
1617
1644
|
return data
|
|
@@ -1625,7 +1652,11 @@ class Deserializer(object):
|
|
|
1625
1652
|
if isinstance(data, self.deserialize_expected_types.get(data_type, tuple())):
|
|
1626
1653
|
return data
|
|
1627
1654
|
|
|
1628
|
-
is_a_text_parsing_type = lambda x: x not in [
|
|
1655
|
+
is_a_text_parsing_type = lambda x: x not in [ # pylint: disable=unnecessary-lambda-assignment
|
|
1656
|
+
"object",
|
|
1657
|
+
"[]",
|
|
1658
|
+
r"{}",
|
|
1659
|
+
]
|
|
1629
1660
|
if isinstance(data, ET.Element) and is_a_text_parsing_type(data_type) and not data.text:
|
|
1630
1661
|
return None
|
|
1631
1662
|
data_val = self.deserialize_type[data_type](data)
|
|
@@ -1645,14 +1676,14 @@ class Deserializer(object):
|
|
|
1645
1676
|
msg = "Unable to deserialize response data."
|
|
1646
1677
|
msg += " Data: {}, {}".format(data, data_type)
|
|
1647
1678
|
raise DeserializationError(msg) from err
|
|
1648
|
-
|
|
1649
|
-
return self._deserialize(obj_type, data)
|
|
1679
|
+
return self._deserialize(obj_type, data)
|
|
1650
1680
|
|
|
1651
1681
|
def deserialize_iter(self, attr, iter_type):
|
|
1652
1682
|
"""Deserialize an iterable.
|
|
1653
1683
|
|
|
1654
1684
|
:param list attr: Iterable to be deserialized.
|
|
1655
1685
|
:param str iter_type: The type of object in the iterable.
|
|
1686
|
+
:return: Deserialized iterable.
|
|
1656
1687
|
:rtype: list
|
|
1657
1688
|
"""
|
|
1658
1689
|
if attr is None:
|
|
@@ -1669,6 +1700,7 @@ class Deserializer(object):
|
|
|
1669
1700
|
:param dict/list attr: Dictionary to be deserialized. Also accepts
|
|
1670
1701
|
a list of key, value pairs.
|
|
1671
1702
|
:param str dict_type: The object type of the items in the dictionary.
|
|
1703
|
+
:return: Deserialized dictionary.
|
|
1672
1704
|
:rtype: dict
|
|
1673
1705
|
"""
|
|
1674
1706
|
if isinstance(attr, list):
|
|
@@ -1679,13 +1711,14 @@ class Deserializer(object):
|
|
|
1679
1711
|
attr = {el.tag: el.text for el in attr}
|
|
1680
1712
|
return {k: self.deserialize_data(v, dict_type) for k, v in attr.items()}
|
|
1681
1713
|
|
|
1682
|
-
def deserialize_object(self, attr, **kwargs):
|
|
1714
|
+
def deserialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements
|
|
1683
1715
|
"""Deserialize a generic object.
|
|
1684
1716
|
This will be handled as a dictionary.
|
|
1685
1717
|
|
|
1686
1718
|
:param dict attr: Dictionary to be deserialized.
|
|
1719
|
+
:return: Deserialized object.
|
|
1687
1720
|
:rtype: dict
|
|
1688
|
-
:raises
|
|
1721
|
+
:raises TypeError: if non-builtin datatype encountered.
|
|
1689
1722
|
"""
|
|
1690
1723
|
if attr is None:
|
|
1691
1724
|
return None
|
|
@@ -1718,11 +1751,10 @@ class Deserializer(object):
|
|
|
1718
1751
|
pass
|
|
1719
1752
|
return deserialized
|
|
1720
1753
|
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
raise TypeError(error + str(obj_type))
|
|
1754
|
+
error = "Cannot deserialize generic object with type: "
|
|
1755
|
+
raise TypeError(error + str(obj_type))
|
|
1724
1756
|
|
|
1725
|
-
def deserialize_basic(self, attr, data_type):
|
|
1757
|
+
def deserialize_basic(self, attr, data_type): # pylint: disable=too-many-return-statements
|
|
1726
1758
|
"""Deserialize basic builtin data type from string.
|
|
1727
1759
|
Will attempt to convert to str, int, float and bool.
|
|
1728
1760
|
This function will also accept '1', '0', 'true' and 'false' as
|
|
@@ -1730,8 +1762,9 @@ class Deserializer(object):
|
|
|
1730
1762
|
|
|
1731
1763
|
:param str attr: response string to be deserialized.
|
|
1732
1764
|
:param str data_type: deserialization data type.
|
|
1765
|
+
:return: Deserialized basic type.
|
|
1733
1766
|
:rtype: str, int, float or bool
|
|
1734
|
-
:raises
|
|
1767
|
+
:raises TypeError: if string format is not valid or data_type is not one of str, int, float, bool.
|
|
1735
1768
|
"""
|
|
1736
1769
|
# If we're here, data is supposed to be a basic type.
|
|
1737
1770
|
# If it's still an XML node, take the text
|
|
@@ -1741,24 +1774,27 @@ class Deserializer(object):
|
|
|
1741
1774
|
if data_type == "str":
|
|
1742
1775
|
# None or '', node <a/> is empty string.
|
|
1743
1776
|
return ""
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
return None
|
|
1777
|
+
# None or '', node <a/> with a strong type is None.
|
|
1778
|
+
# Don't try to model "empty bool" or "empty int"
|
|
1779
|
+
return None
|
|
1748
1780
|
|
|
1749
1781
|
if data_type == "bool":
|
|
1750
1782
|
if attr in [True, False, 1, 0]:
|
|
1751
1783
|
return bool(attr)
|
|
1752
|
-
|
|
1784
|
+
if isinstance(attr, str):
|
|
1753
1785
|
if attr.lower() in ["true", "1"]:
|
|
1754
1786
|
return True
|
|
1755
|
-
|
|
1787
|
+
if attr.lower() in ["false", "0"]:
|
|
1756
1788
|
return False
|
|
1757
1789
|
raise TypeError("Invalid boolean value: {}".format(attr))
|
|
1758
1790
|
|
|
1759
1791
|
if data_type == "str":
|
|
1760
1792
|
return self.deserialize_unicode(attr)
|
|
1761
|
-
|
|
1793
|
+
if data_type == "int":
|
|
1794
|
+
return int(attr)
|
|
1795
|
+
if data_type == "float":
|
|
1796
|
+
return float(attr)
|
|
1797
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
1762
1798
|
|
|
1763
1799
|
@staticmethod
|
|
1764
1800
|
def deserialize_unicode(data):
|
|
@@ -1766,6 +1802,7 @@ class Deserializer(object):
|
|
|
1766
1802
|
as a string.
|
|
1767
1803
|
|
|
1768
1804
|
:param str data: response string to be deserialized.
|
|
1805
|
+
:return: Deserialized string.
|
|
1769
1806
|
:rtype: str or unicode
|
|
1770
1807
|
"""
|
|
1771
1808
|
# We might be here because we have an enum modeled as string,
|
|
@@ -1779,8 +1816,7 @@ class Deserializer(object):
|
|
|
1779
1816
|
return data
|
|
1780
1817
|
except NameError:
|
|
1781
1818
|
return str(data)
|
|
1782
|
-
|
|
1783
|
-
return str(data)
|
|
1819
|
+
return str(data)
|
|
1784
1820
|
|
|
1785
1821
|
@staticmethod
|
|
1786
1822
|
def deserialize_enum(data, enum_obj):
|
|
@@ -1792,6 +1828,7 @@ class Deserializer(object):
|
|
|
1792
1828
|
:param str data: Response string to be deserialized. If this value is
|
|
1793
1829
|
None or invalid it will be returned as-is.
|
|
1794
1830
|
:param Enum enum_obj: Enum object to deserialize to.
|
|
1831
|
+
:return: Deserialized enum object.
|
|
1795
1832
|
:rtype: Enum
|
|
1796
1833
|
"""
|
|
1797
1834
|
if isinstance(data, enum_obj) or data is None:
|
|
@@ -1802,9 +1839,9 @@ class Deserializer(object):
|
|
|
1802
1839
|
# Workaround. We might consider remove it in the future.
|
|
1803
1840
|
try:
|
|
1804
1841
|
return list(enum_obj.__members__.values())[data]
|
|
1805
|
-
except IndexError:
|
|
1842
|
+
except IndexError as exc:
|
|
1806
1843
|
error = "{!r} is not a valid index for enum {!r}"
|
|
1807
|
-
raise DeserializationError(error.format(data, enum_obj))
|
|
1844
|
+
raise DeserializationError(error.format(data, enum_obj)) from exc
|
|
1808
1845
|
try:
|
|
1809
1846
|
return enum_obj(str(data))
|
|
1810
1847
|
except ValueError:
|
|
@@ -1820,8 +1857,9 @@ class Deserializer(object):
|
|
|
1820
1857
|
"""Deserialize string into bytearray.
|
|
1821
1858
|
|
|
1822
1859
|
:param str attr: response string to be deserialized.
|
|
1860
|
+
:return: Deserialized bytearray
|
|
1823
1861
|
:rtype: bytearray
|
|
1824
|
-
:raises
|
|
1862
|
+
:raises TypeError: if string format invalid.
|
|
1825
1863
|
"""
|
|
1826
1864
|
if isinstance(attr, ET.Element):
|
|
1827
1865
|
attr = attr.text
|
|
@@ -1832,8 +1870,9 @@ class Deserializer(object):
|
|
|
1832
1870
|
"""Deserialize base64 encoded string into string.
|
|
1833
1871
|
|
|
1834
1872
|
:param str attr: response string to be deserialized.
|
|
1873
|
+
:return: Deserialized base64 string
|
|
1835
1874
|
:rtype: bytearray
|
|
1836
|
-
:raises
|
|
1875
|
+
:raises TypeError: if string format invalid.
|
|
1837
1876
|
"""
|
|
1838
1877
|
if isinstance(attr, ET.Element):
|
|
1839
1878
|
attr = attr.text
|
|
@@ -1847,8 +1886,9 @@ class Deserializer(object):
|
|
|
1847
1886
|
"""Deserialize string into Decimal object.
|
|
1848
1887
|
|
|
1849
1888
|
:param str attr: response string to be deserialized.
|
|
1850
|
-
:
|
|
1851
|
-
:raises
|
|
1889
|
+
:return: Deserialized decimal
|
|
1890
|
+
:raises DeserializationError: if string format invalid.
|
|
1891
|
+
:rtype: decimal
|
|
1852
1892
|
"""
|
|
1853
1893
|
if isinstance(attr, ET.Element):
|
|
1854
1894
|
attr = attr.text
|
|
@@ -1863,8 +1903,9 @@ class Deserializer(object):
|
|
|
1863
1903
|
"""Deserialize string into long (Py2) or int (Py3).
|
|
1864
1904
|
|
|
1865
1905
|
:param str attr: response string to be deserialized.
|
|
1906
|
+
:return: Deserialized int
|
|
1866
1907
|
:rtype: long or int
|
|
1867
|
-
:raises
|
|
1908
|
+
:raises ValueError: if string format invalid.
|
|
1868
1909
|
"""
|
|
1869
1910
|
if isinstance(attr, ET.Element):
|
|
1870
1911
|
attr = attr.text
|
|
@@ -1875,8 +1916,9 @@ class Deserializer(object):
|
|
|
1875
1916
|
"""Deserialize ISO-8601 formatted string into TimeDelta object.
|
|
1876
1917
|
|
|
1877
1918
|
:param str attr: response string to be deserialized.
|
|
1919
|
+
:return: Deserialized duration
|
|
1878
1920
|
:rtype: TimeDelta
|
|
1879
|
-
:raises
|
|
1921
|
+
:raises DeserializationError: if string format invalid.
|
|
1880
1922
|
"""
|
|
1881
1923
|
if isinstance(attr, ET.Element):
|
|
1882
1924
|
attr = attr.text
|
|
@@ -1885,16 +1927,16 @@ class Deserializer(object):
|
|
|
1885
1927
|
except (ValueError, OverflowError, AttributeError) as err:
|
|
1886
1928
|
msg = "Cannot deserialize duration object."
|
|
1887
1929
|
raise DeserializationError(msg) from err
|
|
1888
|
-
|
|
1889
|
-
return duration
|
|
1930
|
+
return duration
|
|
1890
1931
|
|
|
1891
1932
|
@staticmethod
|
|
1892
1933
|
def deserialize_date(attr):
|
|
1893
1934
|
"""Deserialize ISO-8601 formatted string into Date object.
|
|
1894
1935
|
|
|
1895
1936
|
:param str attr: response string to be deserialized.
|
|
1937
|
+
:return: Deserialized date
|
|
1896
1938
|
:rtype: Date
|
|
1897
|
-
:raises
|
|
1939
|
+
:raises DeserializationError: if string format invalid.
|
|
1898
1940
|
"""
|
|
1899
1941
|
if isinstance(attr, ET.Element):
|
|
1900
1942
|
attr = attr.text
|
|
@@ -1908,8 +1950,9 @@ class Deserializer(object):
|
|
|
1908
1950
|
"""Deserialize ISO-8601 formatted string into time object.
|
|
1909
1951
|
|
|
1910
1952
|
:param str attr: response string to be deserialized.
|
|
1953
|
+
:return: Deserialized time
|
|
1911
1954
|
:rtype: datetime.time
|
|
1912
|
-
:raises
|
|
1955
|
+
:raises DeserializationError: if string format invalid.
|
|
1913
1956
|
"""
|
|
1914
1957
|
if isinstance(attr, ET.Element):
|
|
1915
1958
|
attr = attr.text
|
|
@@ -1922,31 +1965,32 @@ class Deserializer(object):
|
|
|
1922
1965
|
"""Deserialize RFC-1123 formatted string into Datetime object.
|
|
1923
1966
|
|
|
1924
1967
|
:param str attr: response string to be deserialized.
|
|
1968
|
+
:return: Deserialized RFC datetime
|
|
1925
1969
|
:rtype: Datetime
|
|
1926
|
-
:raises
|
|
1970
|
+
:raises DeserializationError: if string format invalid.
|
|
1927
1971
|
"""
|
|
1928
1972
|
if isinstance(attr, ET.Element):
|
|
1929
1973
|
attr = attr.text
|
|
1930
1974
|
try:
|
|
1931
1975
|
parsed_date = email.utils.parsedate_tz(attr) # type: ignore
|
|
1932
1976
|
date_obj = datetime.datetime(
|
|
1933
|
-
*parsed_date[:6], tzinfo=
|
|
1977
|
+
*parsed_date[:6], tzinfo=datetime.timezone(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60))
|
|
1934
1978
|
)
|
|
1935
1979
|
if not date_obj.tzinfo:
|
|
1936
1980
|
date_obj = date_obj.astimezone(tz=TZ_UTC)
|
|
1937
1981
|
except ValueError as err:
|
|
1938
1982
|
msg = "Cannot deserialize to rfc datetime object."
|
|
1939
1983
|
raise DeserializationError(msg) from err
|
|
1940
|
-
|
|
1941
|
-
return date_obj
|
|
1984
|
+
return date_obj
|
|
1942
1985
|
|
|
1943
1986
|
@staticmethod
|
|
1944
1987
|
def deserialize_iso(attr):
|
|
1945
1988
|
"""Deserialize ISO-8601 formatted string into Datetime object.
|
|
1946
1989
|
|
|
1947
1990
|
:param str attr: response string to be deserialized.
|
|
1991
|
+
:return: Deserialized ISO datetime
|
|
1948
1992
|
:rtype: Datetime
|
|
1949
|
-
:raises
|
|
1993
|
+
:raises DeserializationError: if string format invalid.
|
|
1950
1994
|
"""
|
|
1951
1995
|
if isinstance(attr, ET.Element):
|
|
1952
1996
|
attr = attr.text
|
|
@@ -1974,8 +2018,7 @@ class Deserializer(object):
|
|
|
1974
2018
|
except (ValueError, OverflowError, AttributeError) as err:
|
|
1975
2019
|
msg = "Cannot deserialize datetime object."
|
|
1976
2020
|
raise DeserializationError(msg) from err
|
|
1977
|
-
|
|
1978
|
-
return date_obj
|
|
2021
|
+
return date_obj
|
|
1979
2022
|
|
|
1980
2023
|
@staticmethod
|
|
1981
2024
|
def deserialize_unix(attr):
|
|
@@ -1983,8 +2026,9 @@ class Deserializer(object):
|
|
|
1983
2026
|
This is represented as seconds.
|
|
1984
2027
|
|
|
1985
2028
|
:param int attr: Object to be serialized.
|
|
2029
|
+
:return: Deserialized datetime
|
|
1986
2030
|
:rtype: Datetime
|
|
1987
|
-
:raises
|
|
2031
|
+
:raises DeserializationError: if format invalid
|
|
1988
2032
|
"""
|
|
1989
2033
|
if isinstance(attr, ET.Element):
|
|
1990
2034
|
attr = int(attr.text) # type: ignore
|
|
@@ -1994,5 +2038,4 @@ class Deserializer(object):
|
|
|
1994
2038
|
except ValueError as err:
|
|
1995
2039
|
msg = "Cannot deserialize to unix datetime object."
|
|
1996
2040
|
raise DeserializationError(msg) from err
|
|
1997
|
-
|
|
1998
|
-
return date_obj
|
|
2041
|
+
return date_obj
|