@autorest/python 6.2.4 → 6.2.7
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 +14 -2
- package/autorest/codegen/models/client.py +20 -22
- package/autorest/codegen/models/combined_type.py +1 -1
- package/autorest/codegen/models/imports.py +2 -2
- package/autorest/codegen/models/model_type.py +4 -0
- package/autorest/codegen/models/operation.py +6 -6
- package/autorest/codegen/models/operation_group.py +12 -11
- package/autorest/codegen/models/primitive_types.py +50 -0
- package/autorest/codegen/models/property.py +4 -0
- package/autorest/codegen/models/request_builder.py +9 -7
- package/autorest/codegen/serializers/__init__.py +7 -6
- package/autorest/codegen/serializers/builder_serializer.py +67 -30
- package/autorest/codegen/serializers/client_serializer.py +22 -8
- package/autorest/codegen/serializers/metadata_serializer.py +7 -1
- package/autorest/codegen/serializers/model_serializer.py +12 -13
- 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 +171 -130
- package/autorest/codegen/templates/model_dpg.py.jinja2 +1 -1
- package/autorest/codegen/templates/packaging_templates/setup.py.jinja2 +1 -0
- package/autorest/codegen/templates/request_builder.py.jinja2 +1 -1
- package/autorest/codegen/templates/serialization.py.jinja2 +286 -325
- package/autorest/jsonrpc/__init__.py +3 -1
- package/autorest/jsonrpc/localapi.py +3 -1
- package/autorest/jsonrpc/stdstream.py +1 -1
- package/autorest/m2r/__init__.py +2 -2
- package/autorest/multiapi/models/imports.py +34 -22
- 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/autorest/preprocess/__init__.py +7 -1
- package/autorest/preprocess/helpers.py +14 -2
- package/autorest/preprocess/python_mappings.py +27 -0
- package/package.json +2 -2
- package/setup.py +3 -0
|
@@ -17,6 +17,7 @@ from datetime import datetime, date, time, timedelta
|
|
|
17
17
|
from azure.core.utils._utils import _FixedOffset
|
|
18
18
|
from collections.abc import MutableMapping
|
|
19
19
|
from azure.core.exceptions import DeserializationError
|
|
20
|
+
from azure.core import CaseInsensitiveEnumMeta
|
|
20
21
|
import copy
|
|
21
22
|
|
|
22
23
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -40,8 +41,7 @@ with no data. This gets serialized to `null` on the wire.
|
|
|
40
41
|
"""
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
def _timedelta_as_isostr(td):
|
|
44
|
-
# type: (timedelta) -> str
|
|
44
|
+
def _timedelta_as_isostr(td: timedelta) -> str:
|
|
45
45
|
"""Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
|
|
46
46
|
|
|
47
47
|
Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
|
|
@@ -91,8 +91,7 @@ def _timedelta_as_isostr(td):
|
|
|
91
91
|
return "P" + date_str + time_str
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def _datetime_as_isostr(dt):
|
|
95
|
-
# type: (typing.Union[datetime, date, time, timedelta]) -> str
|
|
94
|
+
def _datetime_as_isostr(dt: typing.Union[datetime, date, time, timedelta]) -> str:
|
|
96
95
|
"""Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string"""
|
|
97
96
|
# First try datetime.datetime
|
|
98
97
|
if hasattr(dt, "year") and hasattr(dt, "hour"):
|
|
@@ -121,9 +120,11 @@ try:
|
|
|
121
120
|
except ImportError:
|
|
122
121
|
TZ_UTC = _FixedOffset(0) # type: ignore
|
|
123
122
|
|
|
123
|
+
|
|
124
124
|
def _serialize_bytes(o) -> str:
|
|
125
125
|
return base64.b64encode(o).decode()
|
|
126
126
|
|
|
127
|
+
|
|
127
128
|
def _serialize_datetime(o):
|
|
128
129
|
if hasattr(o, "year") and hasattr(o, "hour"):
|
|
129
130
|
# astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set)
|
|
@@ -136,12 +137,14 @@ def _serialize_datetime(o):
|
|
|
136
137
|
# Next try datetime.date or datetime.time
|
|
137
138
|
return o.isoformat()
|
|
138
139
|
|
|
140
|
+
|
|
139
141
|
def _is_readonly(p):
|
|
140
142
|
try:
|
|
141
143
|
return p._readonly
|
|
142
144
|
except AttributeError:
|
|
143
145
|
return False
|
|
144
146
|
|
|
147
|
+
|
|
145
148
|
class AzureJSONEncoder(JSONEncoder):
|
|
146
149
|
"""A JSON encoder that's capable of serializing datetime objects and bytes."""
|
|
147
150
|
|
|
@@ -170,9 +173,8 @@ class AzureJSONEncoder(JSONEncoder):
|
|
|
170
173
|
return super(AzureJSONEncoder, self).default(o)
|
|
171
174
|
|
|
172
175
|
|
|
173
|
-
_VALID_DATE = re.compile(
|
|
174
|
-
|
|
175
|
-
r'\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?')
|
|
176
|
+
_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}]?")
|
|
177
|
+
|
|
176
178
|
|
|
177
179
|
def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
178
180
|
"""Deserialize ISO-8601 formatted string into Datetime object.
|
|
@@ -188,7 +190,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
188
190
|
if not match:
|
|
189
191
|
raise ValueError("Invalid datetime string: " + attr)
|
|
190
192
|
|
|
191
|
-
check_decimal = attr.split(
|
|
193
|
+
check_decimal = attr.split(".")
|
|
192
194
|
if len(check_decimal) > 1:
|
|
193
195
|
decimal_str = ""
|
|
194
196
|
for digit in check_decimal[1]:
|
|
@@ -205,6 +207,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
205
207
|
raise OverflowError("Hit max or min date")
|
|
206
208
|
return date_obj
|
|
207
209
|
|
|
210
|
+
|
|
208
211
|
def _deserialize_date(attr: typing.Union[str, date]) -> date:
|
|
209
212
|
"""Deserialize ISO-8601 formatted string into Date object.
|
|
210
213
|
:param str attr: response string to be deserialized.
|
|
@@ -215,6 +218,7 @@ def _deserialize_date(attr: typing.Union[str, date]) -> date:
|
|
|
215
218
|
return attr
|
|
216
219
|
return isodate.parse_date(attr, defaultmonth=None, defaultday=None)
|
|
217
220
|
|
|
221
|
+
|
|
218
222
|
def _deserialize_time(attr: typing.Union[str, time]) -> time:
|
|
219
223
|
"""Deserialize ISO-8601 formatted string into time object.
|
|
220
224
|
|
|
@@ -225,18 +229,31 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
|
|
|
225
229
|
return attr
|
|
226
230
|
return isodate.parse_time(attr)
|
|
227
231
|
|
|
232
|
+
|
|
228
233
|
def deserialize_bytes(attr):
|
|
234
|
+
if isinstance(attr, (bytes, bytearray)):
|
|
235
|
+
return attr
|
|
229
236
|
return bytes(base64.b64decode(attr))
|
|
230
237
|
|
|
238
|
+
|
|
239
|
+
def deserialize_duration(attr):
|
|
240
|
+
if isinstance(attr, timedelta):
|
|
241
|
+
return attr
|
|
242
|
+
return isodate.parse_duration(attr)
|
|
243
|
+
|
|
244
|
+
|
|
231
245
|
_DESERIALIZE_MAPPING = {
|
|
232
246
|
datetime: _deserialize_datetime,
|
|
233
247
|
date: _deserialize_date,
|
|
234
248
|
time: _deserialize_time,
|
|
235
249
|
bytes: deserialize_bytes,
|
|
250
|
+
timedelta: deserialize_duration,
|
|
251
|
+
typing.Any: lambda x: x,
|
|
236
252
|
}
|
|
237
253
|
|
|
254
|
+
|
|
238
255
|
def _get_model(module_name: str, model_name: str):
|
|
239
|
-
module_end = module_name.rsplit(
|
|
256
|
+
module_end = module_name.rsplit(".", 1)[0]
|
|
240
257
|
module = sys.modules[module_end]
|
|
241
258
|
models = {k: v for k, v in module.__dict__.items() if isinstance(v, type)}
|
|
242
259
|
if model_name not in models:
|
|
@@ -244,10 +261,11 @@ def _get_model(module_name: str, model_name: str):
|
|
|
244
261
|
return model_name
|
|
245
262
|
return models[model_name]
|
|
246
263
|
|
|
264
|
+
|
|
247
265
|
_UNSET = object()
|
|
248
266
|
|
|
249
|
-
class _MyMutableMapping(MutableMapping):
|
|
250
267
|
|
|
268
|
+
class _MyMutableMapping(MutableMapping):
|
|
251
269
|
def __init__(self, data: typing.Dict[str, typing.Any]) -> None:
|
|
252
270
|
self._data = copy.deepcopy(data)
|
|
253
271
|
|
|
@@ -332,9 +350,11 @@ class _MyMutableMapping(MutableMapping):
|
|
|
332
350
|
def __repr__(self) -> str:
|
|
333
351
|
return str(self._data)
|
|
334
352
|
|
|
353
|
+
|
|
335
354
|
def _is_model(obj: typing.Any) -> bool:
|
|
336
355
|
return getattr(obj, "_is_model", False)
|
|
337
356
|
|
|
357
|
+
|
|
338
358
|
def _serialize(o):
|
|
339
359
|
if isinstance(o, (bytes, bytearray)):
|
|
340
360
|
return _serialize_bytes(o)
|
|
@@ -351,7 +371,10 @@ def _serialize(o):
|
|
|
351
371
|
pass
|
|
352
372
|
return o
|
|
353
373
|
|
|
354
|
-
|
|
374
|
+
|
|
375
|
+
def _get_rest_field(
|
|
376
|
+
attr_to_rest_field: typing.Dict[str, "_RestField"], rest_name: str
|
|
377
|
+
) -> typing.Optional["_RestField"]:
|
|
355
378
|
try:
|
|
356
379
|
return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name)
|
|
357
380
|
except StopIteration:
|
|
@@ -361,8 +384,10 @@ def _get_rest_field(attr_to_rest_field: typing.Dict[str, "_RestField"], rest_nam
|
|
|
361
384
|
def _create_value(rest_field: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
|
|
362
385
|
return _deserialize(rest_field._type, value) if (rest_field and rest_field._is_model) else _serialize(value)
|
|
363
386
|
|
|
387
|
+
|
|
364
388
|
class Model(_MyMutableMapping):
|
|
365
389
|
_is_model = True
|
|
390
|
+
|
|
366
391
|
def __init__(self, *args, **kwargs):
|
|
367
392
|
class_name = self.__class__.__name__
|
|
368
393
|
if len(args) > 1:
|
|
@@ -373,20 +398,15 @@ class Model(_MyMutableMapping):
|
|
|
373
398
|
if rest_field._default is not _UNSET
|
|
374
399
|
}
|
|
375
400
|
if args:
|
|
376
|
-
dict_to_pass.update(
|
|
377
|
-
k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v)
|
|
378
|
-
|
|
379
|
-
})
|
|
401
|
+
dict_to_pass.update(
|
|
402
|
+
{k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()}
|
|
403
|
+
)
|
|
380
404
|
else:
|
|
381
405
|
non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field]
|
|
382
406
|
if non_attr_kwargs:
|
|
383
407
|
# actual type errors only throw the first wrong keyword arg they see, so following that.
|
|
384
|
-
raise TypeError(
|
|
385
|
-
|
|
386
|
-
)
|
|
387
|
-
dict_to_pass.update({
|
|
388
|
-
self._attr_to_rest_field[k]._rest_name: _serialize(v) for k, v in kwargs.items()
|
|
389
|
-
})
|
|
408
|
+
raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'")
|
|
409
|
+
dict_to_pass.update({self._attr_to_rest_field[k]._rest_name: _serialize(v) for k, v in kwargs.items()})
|
|
390
410
|
super().__init__(dict_to_pass)
|
|
391
411
|
|
|
392
412
|
def copy(self):
|
|
@@ -394,15 +414,14 @@ class Model(_MyMutableMapping):
|
|
|
394
414
|
|
|
395
415
|
def __new__(cls, *args: typing.Any, **kwargs: typing.Any):
|
|
396
416
|
# we know the last three classes in mro are going to be 'Model', 'dict', and 'object'
|
|
397
|
-
mros = cls.__mro__[:-3][::-1]
|
|
398
|
-
attr_to_rest_field: typing.Dict[str, _RestField] = {
|
|
399
|
-
k: v
|
|
400
|
-
for mro_class in mros
|
|
401
|
-
for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
|
|
417
|
+
mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
|
|
418
|
+
attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
|
|
419
|
+
k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
|
|
402
420
|
}
|
|
403
421
|
annotations = {
|
|
404
422
|
k: v
|
|
405
|
-
for mro_class in mros
|
|
423
|
+
for mro_class in mros
|
|
424
|
+
if hasattr(mro_class, "__annotations__")
|
|
406
425
|
for k, v in mro_class.__annotations__.items()
|
|
407
426
|
}
|
|
408
427
|
for attr, rest_field in attr_to_rest_field.items():
|
|
@@ -411,18 +430,15 @@ class Model(_MyMutableMapping):
|
|
|
411
430
|
rest_field._type = rest_field._get_deserialize_callable_from_annotation(annotations.get(attr, None))
|
|
412
431
|
if not rest_field._rest_name_input:
|
|
413
432
|
rest_field._rest_name_input = attr
|
|
414
|
-
cls._attr_to_rest_field: typing.Dict[str, _RestField] = {
|
|
415
|
-
k: v
|
|
416
|
-
for k, v in attr_to_rest_field.items()
|
|
417
|
-
}
|
|
433
|
+
cls._attr_to_rest_field: typing.Dict[str, _RestField] = {k: v for k, v in attr_to_rest_field.items()}
|
|
418
434
|
|
|
419
435
|
return super().__new__(cls)
|
|
420
436
|
|
|
421
437
|
def __init_subclass__(cls, discriminator=None):
|
|
422
438
|
for base in cls.__bases__:
|
|
423
|
-
if hasattr(base,
|
|
439
|
+
if hasattr(base, "__mapping__"):
|
|
424
440
|
base.__mapping__[discriminator or cls.__name__] = cls
|
|
425
|
-
|
|
441
|
+
|
|
426
442
|
@classmethod
|
|
427
443
|
def _get_discriminator(cls) -> typing.Optional[str]:
|
|
428
444
|
for v in cls.__dict__.values():
|
|
@@ -432,81 +448,29 @@ class Model(_MyMutableMapping):
|
|
|
432
448
|
|
|
433
449
|
@classmethod
|
|
434
450
|
def _deserialize(cls, data):
|
|
435
|
-
if not hasattr(cls,
|
|
451
|
+
if not hasattr(cls, "__mapping__"):
|
|
436
452
|
return cls(data)
|
|
437
453
|
discriminator = cls._get_discriminator()
|
|
438
|
-
mapped_cls = cls.__mapping__.get(data.get(discriminator),cls)
|
|
454
|
+
mapped_cls = cls.__mapping__.get(data.get(discriminator), cls)
|
|
439
455
|
if mapped_cls == cls:
|
|
440
456
|
return cls(data)
|
|
441
457
|
return mapped_cls._deserialize(data)
|
|
442
458
|
|
|
443
459
|
|
|
444
|
-
def
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return None
|
|
448
|
-
if isinstance(deserializer, type) and issubclass(deserializer, Model):
|
|
449
|
-
return deserializer._deserialize(value)
|
|
450
|
-
return deserializer(value) if deserializer else value
|
|
451
|
-
except Exception as e:
|
|
452
|
-
raise DeserializationError() from e
|
|
453
|
-
|
|
454
|
-
class _RestField:
|
|
455
|
-
def __init__(
|
|
456
|
-
self,
|
|
457
|
-
*,
|
|
458
|
-
name: typing.Optional[str] = None,
|
|
459
|
-
type: typing.Optional[typing.Callable] = None,
|
|
460
|
-
is_discriminator: bool = False,
|
|
461
|
-
readonly: bool = False,
|
|
462
|
-
default: typing.Any = _UNSET,
|
|
463
|
-
):
|
|
464
|
-
self._type = type
|
|
465
|
-
self._rest_name_input = name
|
|
466
|
-
self._module: typing.Optional[str] = None
|
|
467
|
-
self._is_discriminator = is_discriminator
|
|
468
|
-
self._readonly = readonly
|
|
469
|
-
self._is_model = False
|
|
470
|
-
self._default = default
|
|
471
|
-
|
|
472
|
-
@property
|
|
473
|
-
def _rest_name(self) -> str:
|
|
474
|
-
if self._rest_name_input is None:
|
|
475
|
-
raise ValueError("Rest name was never set")
|
|
476
|
-
return self._rest_name_input
|
|
477
|
-
|
|
478
|
-
def __get__(self, obj: Model, type=None):
|
|
479
|
-
# by this point, type and rest_name will have a value bc we default
|
|
480
|
-
# them in __new__ of the Model class
|
|
481
|
-
item = obj.get(self._rest_name)
|
|
482
|
-
if item is None:
|
|
483
|
-
return item
|
|
484
|
-
return _deserialize(self._type, _serialize(item))
|
|
485
|
-
|
|
486
|
-
def __set__(self, obj: Model, value) -> None:
|
|
487
|
-
if value is None:
|
|
488
|
-
# we want to wipe out entries if users set attr to None
|
|
489
|
-
try:
|
|
490
|
-
obj.__delitem__(self._rest_name)
|
|
491
|
-
except KeyError:
|
|
492
|
-
pass
|
|
493
|
-
return
|
|
494
|
-
if self._is_model and not _is_model(value):
|
|
495
|
-
obj.__setitem__(self._rest_name, _deserialize(self._type, value))
|
|
496
|
-
obj.__setitem__(self._rest_name, _serialize(value))
|
|
497
|
-
|
|
498
|
-
def _get_deserialize_callable_from_annotation(self, annotation: typing.Any) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
|
|
460
|
+
def _get_deserialize_callable_from_annotation(
|
|
461
|
+
annotation: typing.Any, module: str,
|
|
462
|
+
) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
|
|
499
463
|
if not annotation or annotation in [int, float]:
|
|
500
464
|
return None
|
|
501
465
|
|
|
502
466
|
try:
|
|
503
|
-
if _is_model(_get_model(
|
|
504
|
-
self._is_model = True
|
|
467
|
+
if _is_model(_get_model(module, annotation)):
|
|
505
468
|
def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
|
|
506
469
|
if _is_model(obj):
|
|
507
470
|
return obj
|
|
508
471
|
return _deserialize(model_deserializer, obj)
|
|
509
|
-
|
|
472
|
+
|
|
473
|
+
return functools.partial(_deserialize_model, _get_model(module, annotation))
|
|
510
474
|
except Exception:
|
|
511
475
|
pass
|
|
512
476
|
|
|
@@ -517,49 +481,61 @@ class _RestField:
|
|
|
517
481
|
except AttributeError:
|
|
518
482
|
pass
|
|
519
483
|
|
|
484
|
+
if isinstance(annotation, typing._GenericAlias): # pylint: disable=protected-access
|
|
485
|
+
if annotation.__origin__ is typing.Union:
|
|
486
|
+
def _deserialize_with_union(union_annotation: typing._GenericAlias, obj):
|
|
487
|
+
for t in union_annotation.__args__:
|
|
488
|
+
try:
|
|
489
|
+
return _deserialize(t, obj)
|
|
490
|
+
except DeserializationError:
|
|
491
|
+
pass
|
|
492
|
+
raise DeserializationError()
|
|
493
|
+
return functools.partial(_deserialize_with_union, annotation)
|
|
494
|
+
|
|
520
495
|
# is it optional?
|
|
521
496
|
try:
|
|
522
497
|
# right now, assuming we don't have unions, since we're getting rid of the only
|
|
523
498
|
# union we used to have in msrest models, which was union of str and enum
|
|
524
499
|
if any(a for a in annotation.__args__ if a == type(None)):
|
|
525
500
|
|
|
526
|
-
if_obj_deserializer =
|
|
527
|
-
next(a for
|
|
501
|
+
if_obj_deserializer = _get_deserialize_callable_from_annotation(
|
|
502
|
+
next(a for a in annotation.__args__ if a != type(None)), module
|
|
528
503
|
)
|
|
504
|
+
|
|
529
505
|
def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
|
|
530
506
|
if obj is None:
|
|
531
507
|
return obj
|
|
532
|
-
return
|
|
508
|
+
return _deserialize_with_callable(if_obj_deserializer, obj)
|
|
533
509
|
|
|
534
510
|
return functools.partial(_deserialize_with_optional, if_obj_deserializer)
|
|
535
511
|
except (AttributeError):
|
|
536
512
|
pass
|
|
537
513
|
|
|
538
|
-
|
|
539
514
|
# is it a forward ref / in quotes?
|
|
540
515
|
if isinstance(annotation, str) or type(annotation) == typing.ForwardRef:
|
|
541
516
|
try:
|
|
542
517
|
model_name = annotation.__forward_arg__ # type: ignore
|
|
543
518
|
except AttributeError:
|
|
544
519
|
model_name = annotation
|
|
545
|
-
if
|
|
546
|
-
annotation = _get_model(
|
|
520
|
+
if module is not None:
|
|
521
|
+
annotation = _get_model(module, model_name)
|
|
547
522
|
|
|
548
523
|
try:
|
|
549
524
|
if annotation._name == "Dict":
|
|
550
|
-
key_deserializer =
|
|
551
|
-
value_deserializer =
|
|
525
|
+
key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
|
|
526
|
+
value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module)
|
|
527
|
+
|
|
552
528
|
def _deserialize_dict(
|
|
553
529
|
key_deserializer: typing.Optional[typing.Callable],
|
|
554
530
|
value_deserializer: typing.Optional[typing.Callable],
|
|
555
|
-
obj: typing.Dict[typing.Any, typing.Any]
|
|
531
|
+
obj: typing.Dict[typing.Any, typing.Any],
|
|
556
532
|
):
|
|
557
533
|
if obj is None:
|
|
558
534
|
return obj
|
|
559
535
|
return {
|
|
560
|
-
_deserialize(key_deserializer, k): _deserialize(value_deserializer, v)
|
|
561
|
-
for k, v in obj.items()
|
|
536
|
+
_deserialize(key_deserializer, k): _deserialize(value_deserializer, v) for k, v in obj.items()
|
|
562
537
|
}
|
|
538
|
+
|
|
563
539
|
return functools.partial(
|
|
564
540
|
_deserialize_dict,
|
|
565
541
|
key_deserializer,
|
|
@@ -570,38 +546,31 @@ class _RestField:
|
|
|
570
546
|
try:
|
|
571
547
|
if annotation._name in ["List", "Set", "Tuple", "Sequence"]:
|
|
572
548
|
if len(annotation.__args__) > 1:
|
|
549
|
+
|
|
573
550
|
def _deserialize_multiple_sequence(
|
|
574
|
-
entry_deserializers: typing.List[typing.Optional[typing.Callable]],
|
|
575
|
-
obj
|
|
551
|
+
entry_deserializers: typing.List[typing.Optional[typing.Callable]], obj
|
|
576
552
|
):
|
|
577
553
|
if obj is None:
|
|
578
554
|
return obj
|
|
579
555
|
return type(obj)(
|
|
580
|
-
_deserialize(deserializer, entry)
|
|
581
|
-
for entry, deserializer in zip(obj, entry_deserializers)
|
|
556
|
+
_deserialize(deserializer, entry) for entry, deserializer in zip(obj, entry_deserializers)
|
|
582
557
|
)
|
|
558
|
+
|
|
583
559
|
entry_deserializers = [
|
|
584
|
-
|
|
585
|
-
for dt in annotation.__args__
|
|
560
|
+
_get_deserialize_callable_from_annotation(dt, module) for dt in annotation.__args__
|
|
586
561
|
]
|
|
587
|
-
return functools.partial(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
)
|
|
591
|
-
deserializer = self._get_deserialize_callable_from_annotation(annotation.__args__[0])
|
|
562
|
+
return functools.partial(_deserialize_multiple_sequence, entry_deserializers)
|
|
563
|
+
deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
|
|
564
|
+
|
|
592
565
|
def _deserialize_sequence(
|
|
593
566
|
deserializer: typing.Optional[typing.Callable],
|
|
594
567
|
obj,
|
|
595
568
|
):
|
|
596
569
|
if obj is None:
|
|
597
570
|
return obj
|
|
598
|
-
return type(obj)(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
return functools.partial(
|
|
602
|
-
_deserialize_sequence,
|
|
603
|
-
deserializer
|
|
604
|
-
)
|
|
571
|
+
return type(obj)(_deserialize(deserializer, entry) for entry in obj)
|
|
572
|
+
|
|
573
|
+
return functools.partial(_deserialize_sequence, deserializer)
|
|
605
574
|
except (TypeError, IndexError, AttributeError, SyntaxError):
|
|
606
575
|
pass
|
|
607
576
|
|
|
@@ -613,15 +582,84 @@ class _RestField:
|
|
|
613
582
|
if obj is None:
|
|
614
583
|
return obj
|
|
615
584
|
try:
|
|
616
|
-
return
|
|
585
|
+
return _deserialize_with_callable(annotation, obj)
|
|
617
586
|
except Exception:
|
|
618
587
|
pass
|
|
619
|
-
return
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
588
|
+
return _deserialize_with_callable(deserializer_from_mapping, obj)
|
|
589
|
+
|
|
590
|
+
return functools.partial(_deserialize_default, annotation, _DESERIALIZE_MAPPING.get(annotation))
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
|
|
594
|
+
try:
|
|
595
|
+
if value is None:
|
|
596
|
+
return None
|
|
597
|
+
if isinstance(deserializer, CaseInsensitiveEnumMeta):
|
|
598
|
+
try:
|
|
599
|
+
return deserializer(value)
|
|
600
|
+
except ValueError:
|
|
601
|
+
# for unknown value, return raw value
|
|
602
|
+
return value
|
|
603
|
+
if isinstance(deserializer, type) and issubclass(deserializer, Model):
|
|
604
|
+
return deserializer._deserialize(value)
|
|
605
|
+
return deserializer(value) if deserializer else value
|
|
606
|
+
except Exception as e:
|
|
607
|
+
raise DeserializationError() from e
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def _deserialize(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
|
|
611
|
+
deserializer = _get_deserialize_callable_from_annotation(deserializer, "")
|
|
612
|
+
return _deserialize_with_callable(deserializer, value)
|
|
613
|
+
|
|
614
|
+
class _RestField:
|
|
615
|
+
def __init__(
|
|
616
|
+
self,
|
|
617
|
+
*,
|
|
618
|
+
name: typing.Optional[str] = None,
|
|
619
|
+
type: typing.Optional[typing.Callable] = None,
|
|
620
|
+
is_discriminator: bool = False,
|
|
621
|
+
readonly: bool = False,
|
|
622
|
+
default: typing.Any = _UNSET,
|
|
623
|
+
):
|
|
624
|
+
self._type = type
|
|
625
|
+
self._rest_name_input = name
|
|
626
|
+
self._module: typing.Optional[str] = None
|
|
627
|
+
self._is_discriminator = is_discriminator
|
|
628
|
+
self._readonly = readonly
|
|
629
|
+
self._is_model = False
|
|
630
|
+
self._default = default
|
|
631
|
+
|
|
632
|
+
@property
|
|
633
|
+
def _rest_name(self) -> str:
|
|
634
|
+
if self._rest_name_input is None:
|
|
635
|
+
raise ValueError("Rest name was never set")
|
|
636
|
+
return self._rest_name_input
|
|
637
|
+
|
|
638
|
+
def __get__(self, obj: Model, type=None):
|
|
639
|
+
# by this point, type and rest_name will have a value bc we default
|
|
640
|
+
# them in __new__ of the Model class
|
|
641
|
+
item = obj.get(self._rest_name)
|
|
642
|
+
if item is None:
|
|
643
|
+
return item
|
|
644
|
+
return _deserialize(self._type, _serialize(item))
|
|
645
|
+
|
|
646
|
+
def __set__(self, obj: Model, value) -> None:
|
|
647
|
+
if value is None:
|
|
648
|
+
# we want to wipe out entries if users set attr to None
|
|
649
|
+
try:
|
|
650
|
+
obj.__delitem__(self._rest_name)
|
|
651
|
+
except KeyError:
|
|
652
|
+
pass
|
|
653
|
+
return
|
|
654
|
+
if self._is_model and not _is_model(value):
|
|
655
|
+
obj.__setitem__(self._rest_name, _deserialize(self._type, value))
|
|
656
|
+
obj.__setitem__(self._rest_name, _serialize(value))
|
|
657
|
+
|
|
658
|
+
def _get_deserialize_callable_from_annotation(
|
|
659
|
+
self, annotation: typing.Any
|
|
660
|
+
) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
|
|
661
|
+
return _get_deserialize_callable_from_annotation(annotation, self._module)
|
|
662
|
+
|
|
625
663
|
|
|
626
664
|
def rest_field(
|
|
627
665
|
*,
|
|
@@ -632,5 +670,8 @@ def rest_field(
|
|
|
632
670
|
) -> typing.Any:
|
|
633
671
|
return _RestField(name=name, type=type, readonly=readonly, default=default)
|
|
634
672
|
|
|
635
|
-
|
|
673
|
+
|
|
674
|
+
def rest_discriminator(
|
|
675
|
+
*, name: typing.Optional[str] = None, type: typing.Optional[typing.Callable] = None
|
|
676
|
+
) -> typing.Any:
|
|
636
677
|
return _RestField(name=name, type=type, is_discriminator=True)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{% endif %}
|
|
10
10
|
{% if model.has_readonly_or_constant_property %}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Readonly variables are only populated by the server, and will be ignored when sending a request.
|
|
13
13
|
{% endif %}
|
|
14
14
|
{% if (model.properties | selectattr('optional', "equalto", false) | first) is defined %}
|
|
15
15
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{{ request_builder_serializer.construct_url(request_builder) }}
|
|
16
16
|
{% if request_builder.parameters.path %}
|
|
17
17
|
{{ op_tools.serialize(request_builder_serializer.serialize_path(request_builder)) | indent }}
|
|
18
|
-
_url = _format_url_section(_url, **path_format_arguments)
|
|
18
|
+
_url: str = _format_url_section(_url, **path_format_arguments) # type: ignore
|
|
19
19
|
{% endif %}
|
|
20
20
|
|
|
21
21
|
{% if request_builder.parameters.query %}
|