@autorest/python 6.2.12 → 6.2.15
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/__init__.py +7 -5
- package/autorest/_utils.py +7 -1
- package/autorest/black/__init__.py +6 -1
- package/autorest/codegen/__init__.py +1 -1
- package/autorest/codegen/models/base.py +4 -13
- package/autorest/codegen/models/client.py +9 -11
- package/autorest/codegen/models/code_model.py +2 -2
- package/autorest/codegen/models/credential_types.py +7 -14
- package/autorest/codegen/models/dictionary_type.py +1 -1
- package/autorest/codegen/models/imports.py +3 -3
- package/autorest/codegen/models/lro_operation.py +5 -5
- package/autorest/codegen/models/model_type.py +11 -8
- package/autorest/codegen/models/operation.py +8 -8
- package/autorest/codegen/models/operation_group.py +3 -1
- package/autorest/codegen/models/paging_operation.py +2 -2
- package/autorest/codegen/models/parameter.py +27 -6
- package/autorest/codegen/models/parameter_list.py +1 -9
- package/autorest/codegen/models/primitive_types.py +1 -1
- package/autorest/codegen/models/property.py +15 -3
- package/autorest/codegen/models/response.py +2 -2
- package/autorest/codegen/serializers/__init__.py +2 -2
- package/autorest/codegen/serializers/builder_serializer.py +55 -25
- package/autorest/codegen/serializers/client_serializer.py +6 -4
- package/autorest/codegen/serializers/general_serializer.py +7 -2
- package/autorest/codegen/serializers/model_serializer.py +14 -4
- package/autorest/codegen/serializers/sample_serializer.py +2 -6
- package/autorest/codegen/templates/config.py.jinja2 +25 -6
- package/autorest/codegen/templates/enum.py.jinja2 +2 -2
- package/autorest/codegen/templates/metadata.json.jinja2 +18 -9
- package/autorest/codegen/templates/model_base.py.jinja2 +74 -63
- package/autorest/codegen/templates/model_dpg.py.jinja2 +6 -4
- package/autorest/codegen/templates/serialization.py.jinja2 +17 -15
- package/autorest/codegen/templates/vendor.py.jinja2 +3 -2
- package/autorest/jsonrpc/localapi.py +3 -3
- package/autorest/jsonrpc/server.py +3 -3
- package/autorest/m2r/__init__.py +1 -1
- package/autorest/m4reformatter/__init__.py +12 -2
- package/autorest/multiapi/models/__init__.py +2 -0
- package/autorest/multiapi/models/code_model.py +13 -0
- package/autorest/multiapi/models/global_parameter.py +1 -0
- package/autorest/multiapi/models/imports.py +3 -3
- package/autorest/multiapi/serializers/__init__.py +30 -3
- package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +8 -12
- package/autorest/postprocess/get_all.py +3 -1
- package/autorest/postprocess/venvtools.py +5 -4
- package/autorest/preprocess/__init__.py +16 -4
- package/autorest/preprocess/python_mappings.py +1 -0
- package/index.js +0 -0
- package/package.json +2 -1
- package/requirements.txt +6 -6
- package/setup.py +0 -1
- package/venvtools.py +2 -3
|
@@ -4,22 +4,23 @@
|
|
|
4
4
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
5
5
|
# license information.
|
|
6
6
|
# --------------------------------------------------------------------------
|
|
7
|
+
# pylint: disable=protected-access, arguments-differ, signature-differs, broad-except
|
|
8
|
+
# pyright: reportGeneralTypeIssues=false
|
|
7
9
|
|
|
8
10
|
import functools
|
|
9
11
|
import sys
|
|
10
12
|
import logging
|
|
11
13
|
import base64
|
|
12
14
|
import re
|
|
13
|
-
import
|
|
14
|
-
from json import JSONEncoder
|
|
15
|
+
import copy
|
|
15
16
|
import typing
|
|
16
|
-
from datetime import datetime, date, time, timedelta
|
|
17
|
-
from azure.core.utils._utils import _FixedOffset
|
|
18
17
|
from collections.abc import MutableMapping
|
|
18
|
+
from datetime import datetime, date, time, timedelta, timezone
|
|
19
|
+
from json import JSONEncoder
|
|
20
|
+
import isodate
|
|
19
21
|
from azure.core.exceptions import DeserializationError
|
|
20
22
|
from azure.core import CaseInsensitiveEnumMeta
|
|
21
23
|
from azure.core.pipeline import PipelineResponse
|
|
22
|
-
import copy
|
|
23
24
|
|
|
24
25
|
_LOGGER = logging.getLogger(__name__)
|
|
25
26
|
|
|
@@ -41,11 +42,16 @@ A falsy sentinel object which is supposed to be used to specify attributes
|
|
|
41
42
|
with no data. This gets serialized to `null` on the wire.
|
|
42
43
|
"""
|
|
43
44
|
|
|
45
|
+
TZ_UTC = timezone.utc
|
|
44
46
|
|
|
45
47
|
def _timedelta_as_isostr(td: timedelta) -> str:
|
|
46
48
|
"""Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
|
|
47
49
|
|
|
48
50
|
Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
|
|
51
|
+
|
|
52
|
+
:param timedelta td: The timedelta to convert
|
|
53
|
+
:rtype: str
|
|
54
|
+
:return: ISO8601 version of this timedelta
|
|
49
55
|
"""
|
|
50
56
|
|
|
51
57
|
# Split seconds to larger units
|
|
@@ -93,7 +99,12 @@ def _timedelta_as_isostr(td: timedelta) -> str:
|
|
|
93
99
|
|
|
94
100
|
|
|
95
101
|
def _datetime_as_isostr(dt: typing.Union[datetime, date, time, timedelta]) -> str:
|
|
96
|
-
"""Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string
|
|
102
|
+
"""Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string
|
|
103
|
+
|
|
104
|
+
:param timedelta dt: The date object to convert
|
|
105
|
+
:rtype: str
|
|
106
|
+
:return: ISO8601 version of this datetime
|
|
107
|
+
"""
|
|
97
108
|
# First try datetime.datetime
|
|
98
109
|
if hasattr(dt, "year") and hasattr(dt, "hour"):
|
|
99
110
|
dt = typing.cast(datetime, dt)
|
|
@@ -113,15 +124,6 @@ def _datetime_as_isostr(dt: typing.Union[datetime, date, time, timedelta]) -> st
|
|
|
113
124
|
dt = typing.cast(timedelta, dt)
|
|
114
125
|
return _timedelta_as_isostr(dt)
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
try:
|
|
118
|
-
from datetime import timezone
|
|
119
|
-
|
|
120
|
-
TZ_UTC = timezone.utc # type: ignore
|
|
121
|
-
except ImportError:
|
|
122
|
-
TZ_UTC = _FixedOffset(0) # type: ignore
|
|
123
|
-
|
|
124
|
-
|
|
125
127
|
def _serialize_bytes(o) -> str:
|
|
126
128
|
return base64.b64encode(o).decode()
|
|
127
129
|
|
|
@@ -141,7 +143,7 @@ def _serialize_datetime(o):
|
|
|
141
143
|
|
|
142
144
|
def _is_readonly(p):
|
|
143
145
|
try:
|
|
144
|
-
return p._readonly
|
|
146
|
+
return p._readonly # pylint: disable=protected-access
|
|
145
147
|
except AttributeError:
|
|
146
148
|
return False
|
|
147
149
|
|
|
@@ -151,7 +153,7 @@ class AzureJSONEncoder(JSONEncoder):
|
|
|
151
153
|
|
|
152
154
|
def default(self, o): # pylint: disable=too-many-return-statements
|
|
153
155
|
if _is_model(o):
|
|
154
|
-
readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
|
|
156
|
+
readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] # pylint: disable=protected-access
|
|
155
157
|
return {k: v for k, v in o.items() if k not in readonly_props}
|
|
156
158
|
if isinstance(o, (bytes, bytearray)):
|
|
157
159
|
return base64.b64encode(o).decode()
|
|
@@ -174,7 +176,7 @@ class AzureJSONEncoder(JSONEncoder):
|
|
|
174
176
|
return super(AzureJSONEncoder, self).default(o)
|
|
175
177
|
|
|
176
178
|
|
|
177
|
-
_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}]?")
|
|
179
|
+
_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}]?")
|
|
178
180
|
|
|
179
181
|
|
|
180
182
|
def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
@@ -182,6 +184,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
182
184
|
|
|
183
185
|
:param str attr: response string to be deserialized.
|
|
184
186
|
:rtype: ~datetime.datetime
|
|
187
|
+
:returns: The datetime object from that input
|
|
185
188
|
"""
|
|
186
189
|
if isinstance(attr, datetime):
|
|
187
190
|
# i'm already deserialized
|
|
@@ -212,7 +215,8 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
212
215
|
def _deserialize_date(attr: typing.Union[str, date]) -> date:
|
|
213
216
|
"""Deserialize ISO-8601 formatted string into Date object.
|
|
214
217
|
:param str attr: response string to be deserialized.
|
|
215
|
-
:rtype:
|
|
218
|
+
:rtype: date
|
|
219
|
+
:returns: The date object from that input
|
|
216
220
|
"""
|
|
217
221
|
# This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception.
|
|
218
222
|
if isinstance(attr, date):
|
|
@@ -225,6 +229,7 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
|
|
|
225
229
|
|
|
226
230
|
:param str attr: response string to be deserialized.
|
|
227
231
|
:rtype: datetime.time
|
|
232
|
+
:returns: The time object from that input
|
|
228
233
|
"""
|
|
229
234
|
if isinstance(attr, time):
|
|
230
235
|
return attr
|
|
@@ -271,7 +276,7 @@ class _MyMutableMapping(MutableMapping):
|
|
|
271
276
|
def __init__(self, data: typing.Dict[str, typing.Any]) -> None:
|
|
272
277
|
self._data = copy.deepcopy(data)
|
|
273
278
|
|
|
274
|
-
def __contains__(self, key:
|
|
279
|
+
def __contains__(self, key: typing.Any) -> bool:
|
|
275
280
|
return key in self._data
|
|
276
281
|
|
|
277
282
|
def __getitem__(self, key: str) -> typing.Any:
|
|
@@ -307,15 +312,15 @@ class _MyMutableMapping(MutableMapping):
|
|
|
307
312
|
except KeyError:
|
|
308
313
|
return default
|
|
309
314
|
|
|
310
|
-
@typing.overload
|
|
311
|
-
def pop(self, key: str) -> typing.Any:
|
|
315
|
+
@typing.overload # type: ignore
|
|
316
|
+
def pop(self, key: str) -> typing.Any: # pylint: disable=no-member
|
|
312
317
|
...
|
|
313
318
|
|
|
314
319
|
@typing.overload
|
|
315
320
|
def pop(self, key: str, default: typing.Any) -> typing.Any:
|
|
316
321
|
...
|
|
317
322
|
|
|
318
|
-
def pop(self, key:
|
|
323
|
+
def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
|
|
319
324
|
if default is _UNSET:
|
|
320
325
|
return self._data.pop(key)
|
|
321
326
|
return self._data.pop(key, default)
|
|
@@ -329,7 +334,7 @@ class _MyMutableMapping(MutableMapping):
|
|
|
329
334
|
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
330
335
|
self._data.update(*args, **kwargs)
|
|
331
336
|
|
|
332
|
-
@typing.overload
|
|
337
|
+
@typing.overload # type: ignore
|
|
333
338
|
def setdefault(self, key: str) -> typing.Any:
|
|
334
339
|
...
|
|
335
340
|
|
|
@@ -337,7 +342,7 @@ class _MyMutableMapping(MutableMapping):
|
|
|
337
342
|
def setdefault(self, key: str, default: typing.Any) -> typing.Any:
|
|
338
343
|
...
|
|
339
344
|
|
|
340
|
-
def setdefault(self, key:
|
|
345
|
+
def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
|
|
341
346
|
if default is _UNSET:
|
|
342
347
|
return self._data.setdefault(key)
|
|
343
348
|
return self._data.setdefault(key, default)
|
|
@@ -383,8 +388,8 @@ def _get_rest_field(
|
|
|
383
388
|
return None
|
|
384
389
|
|
|
385
390
|
|
|
386
|
-
def _create_value(
|
|
387
|
-
return _deserialize(
|
|
391
|
+
def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
|
|
392
|
+
return _deserialize(rf._type, value) if (rf and rf._is_model) else _serialize(value)
|
|
388
393
|
|
|
389
394
|
|
|
390
395
|
class Model(_MyMutableMapping):
|
|
@@ -414,7 +419,7 @@ class Model(_MyMutableMapping):
|
|
|
414
419
|
def copy(self):
|
|
415
420
|
return Model(self.__dict__)
|
|
416
421
|
|
|
417
|
-
def __new__(cls, *args: typing.Any, **kwargs: typing.Any):
|
|
422
|
+
def __new__(cls, *args: typing.Any, **kwargs: typing.Any): # pylint: disable=unused-argument
|
|
418
423
|
# we know the last three classes in mro are going to be 'Model', 'dict', and 'object'
|
|
419
424
|
mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
|
|
420
425
|
attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
|
|
@@ -423,50 +428,50 @@ class Model(_MyMutableMapping):
|
|
|
423
428
|
annotations = {
|
|
424
429
|
k: v
|
|
425
430
|
for mro_class in mros
|
|
426
|
-
if hasattr(mro_class, "__annotations__")
|
|
427
|
-
for k, v in mro_class.__annotations__.items()
|
|
431
|
+
if hasattr(mro_class, "__annotations__") # pylint: disable=no-member
|
|
432
|
+
for k, v in mro_class.__annotations__.items() # pylint: disable=no-member
|
|
428
433
|
}
|
|
429
|
-
for attr,
|
|
430
|
-
|
|
431
|
-
if not
|
|
432
|
-
|
|
433
|
-
if not
|
|
434
|
-
|
|
435
|
-
cls._attr_to_rest_field: typing.Dict[str, _RestField] =
|
|
434
|
+
for attr, rf in attr_to_rest_field.items():
|
|
435
|
+
rf._module = cls.__module__
|
|
436
|
+
if not rf._type:
|
|
437
|
+
rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
|
|
438
|
+
if not rf._rest_name_input:
|
|
439
|
+
rf._rest_name_input = attr
|
|
440
|
+
cls._attr_to_rest_field: typing.Dict[str, _RestField] = dict(attr_to_rest_field.items())
|
|
436
441
|
|
|
437
442
|
return super().__new__(cls)
|
|
438
443
|
|
|
439
444
|
def __init_subclass__(cls, discriminator=None):
|
|
440
445
|
for base in cls.__bases__:
|
|
441
|
-
if hasattr(base, "__mapping__"):
|
|
442
|
-
base.__mapping__[discriminator or cls.__name__] = cls
|
|
446
|
+
if hasattr(base, "__mapping__"): # pylint: disable=no-member
|
|
447
|
+
base.__mapping__[discriminator or cls.__name__] = cls # pylint: disable=no-member
|
|
443
448
|
|
|
444
449
|
@classmethod
|
|
445
450
|
def _get_discriminator(cls) -> typing.Optional[str]:
|
|
446
451
|
for v in cls.__dict__.values():
|
|
447
|
-
if isinstance(v, _RestField) and v._is_discriminator:
|
|
448
|
-
return v._rest_name
|
|
452
|
+
if isinstance(v, _RestField) and v._is_discriminator: # pylint: disable=protected-access
|
|
453
|
+
return v._rest_name # pylint: disable=protected-access
|
|
449
454
|
return None
|
|
450
455
|
|
|
451
456
|
@classmethod
|
|
452
457
|
def _deserialize(cls, data):
|
|
453
|
-
if not hasattr(cls, "__mapping__"):
|
|
458
|
+
if not hasattr(cls, "__mapping__"): # pylint: disable=no-member
|
|
454
459
|
return cls(data)
|
|
455
460
|
discriminator = cls._get_discriminator()
|
|
456
|
-
mapped_cls = cls.__mapping__.get(data.get(discriminator), cls)
|
|
461
|
+
mapped_cls = cls.__mapping__.get(data.get(discriminator), cls) # pylint: disable=no-member
|
|
457
462
|
if mapped_cls == cls:
|
|
458
463
|
return cls(data)
|
|
459
|
-
return mapped_cls._deserialize(data)
|
|
464
|
+
return mapped_cls._deserialize(data) # pylint: disable=protected-access
|
|
460
465
|
|
|
461
466
|
|
|
462
|
-
def _get_deserialize_callable_from_annotation(
|
|
463
|
-
annotation: typing.Any, module: str,
|
|
467
|
+
def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements
|
|
468
|
+
annotation: typing.Any, module: typing.Optional[str],
|
|
464
469
|
) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
|
|
465
470
|
if not annotation or annotation in [int, float]:
|
|
466
471
|
return None
|
|
467
472
|
|
|
468
473
|
try:
|
|
469
|
-
if _is_model(_get_model(module, annotation)):
|
|
474
|
+
if module and _is_model(_get_model(module, annotation)):
|
|
470
475
|
def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
|
|
471
476
|
if _is_model(obj):
|
|
472
477
|
return obj
|
|
@@ -478,21 +483,25 @@ def _get_deserialize_callable_from_annotation(
|
|
|
478
483
|
|
|
479
484
|
# is it a literal?
|
|
480
485
|
try:
|
|
481
|
-
if
|
|
486
|
+
if sys.version_info >= (3, 8):
|
|
487
|
+
from typing import Literal # pylint: disable=no-name-in-module, ungrouped-imports
|
|
488
|
+
else:
|
|
489
|
+
from typing_extensions import Literal # type: ignore # pylint: disable=ungrouped-imports
|
|
490
|
+
|
|
491
|
+
if annotation.__origin__ == Literal:
|
|
482
492
|
return None
|
|
483
493
|
except AttributeError:
|
|
484
494
|
pass
|
|
485
495
|
|
|
486
|
-
if
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
return functools.partial(_deserialize_with_union, annotation)
|
|
496
|
+
if getattr(annotation, "__origin__", None) is typing.Union:
|
|
497
|
+
def _deserialize_with_union(union_annotation, obj):
|
|
498
|
+
for t in union_annotation.__args__:
|
|
499
|
+
try:
|
|
500
|
+
return _deserialize(t, obj, module)
|
|
501
|
+
except DeserializationError:
|
|
502
|
+
pass
|
|
503
|
+
raise DeserializationError()
|
|
504
|
+
return functools.partial(_deserialize_with_union, annotation)
|
|
496
505
|
|
|
497
506
|
# is it optional?
|
|
498
507
|
try:
|
|
@@ -510,11 +519,11 @@ def _get_deserialize_callable_from_annotation(
|
|
|
510
519
|
return _deserialize_with_callable(if_obj_deserializer, obj)
|
|
511
520
|
|
|
512
521
|
return functools.partial(_deserialize_with_optional, if_obj_deserializer)
|
|
513
|
-
except
|
|
522
|
+
except AttributeError:
|
|
514
523
|
pass
|
|
515
524
|
|
|
516
525
|
# is it a forward ref / in quotes?
|
|
517
|
-
if isinstance(annotation, str
|
|
526
|
+
if isinstance(annotation, (str, typing.ForwardRef)):
|
|
518
527
|
try:
|
|
519
528
|
model_name = annotation.__forward_arg__ # type: ignore
|
|
520
529
|
except AttributeError:
|
|
@@ -596,6 +605,8 @@ def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[ty
|
|
|
596
605
|
try:
|
|
597
606
|
if value is None:
|
|
598
607
|
return None
|
|
608
|
+
if deserializer is None:
|
|
609
|
+
return value
|
|
599
610
|
if isinstance(deserializer, CaseInsensitiveEnumMeta):
|
|
600
611
|
try:
|
|
601
612
|
return deserializer(value)
|
|
@@ -603,13 +614,13 @@ def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[ty
|
|
|
603
614
|
# for unknown value, return raw value
|
|
604
615
|
return value
|
|
605
616
|
if isinstance(deserializer, type) and issubclass(deserializer, Model):
|
|
606
|
-
return deserializer._deserialize(value)
|
|
607
|
-
return deserializer(value)
|
|
617
|
+
return deserializer._deserialize(value) # type: ignore
|
|
618
|
+
return deserializer(value)
|
|
608
619
|
except Exception as e:
|
|
609
620
|
raise DeserializationError() from e
|
|
610
621
|
|
|
611
622
|
|
|
612
|
-
def _deserialize(deserializer: typing.
|
|
623
|
+
def _deserialize(deserializer: typing.Any, value: typing.Any, module: typing.Optional[str] = None) -> typing.Any:
|
|
613
624
|
if isinstance(value, PipelineResponse):
|
|
614
625
|
value = value.http_response.json()
|
|
615
626
|
deserializer = _get_deserialize_callable_from_annotation(deserializer, module)
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
30
|
{% if model.is_polymorphic %}
|
|
31
|
-
__mapping__ = {}
|
|
31
|
+
__mapping__: Dict[str, _model_base.Model] = {}
|
|
32
32
|
{% endif %}
|
|
33
33
|
{% for p in serializer.get_properties_to_declare(model)%}
|
|
34
34
|
{% for line in serializer.declare_property(p) %}
|
|
@@ -52,11 +52,13 @@
|
|
|
52
52
|
:param mapping: raw JSON to initialize the model.
|
|
53
53
|
:type mapping: Mapping[str, Any]
|
|
54
54
|
"""
|
|
55
|
-
...
|
|
56
55
|
|
|
57
56
|
{% endif %}
|
|
58
|
-
|
|
57
|
+
{% set initialize_properties = serializer.initialize_properties(model) %}
|
|
58
|
+
{% if model.is_public and serializer.init_line(model) or initialize_properties %}
|
|
59
|
+
def __init__(self, *args, **kwargs):{{ '# pylint: disable=useless-super-delegation' if not initialize_properties else '' }}
|
|
59
60
|
super().__init__(*args, **kwargs)
|
|
60
|
-
{% for initialize_property in
|
|
61
|
+
{% for initialize_property in initialize_properties %}
|
|
61
62
|
{{ initialize_property }}
|
|
62
63
|
{% endfor %}
|
|
64
|
+
{% endif %}
|
|
@@ -50,7 +50,9 @@ from typing import (
|
|
|
50
50
|
Callable,
|
|
51
51
|
TypeVar,
|
|
52
52
|
MutableMapping,
|
|
53
|
-
Type
|
|
53
|
+
Type,
|
|
54
|
+
List,
|
|
55
|
+
Mapping,
|
|
54
56
|
)
|
|
55
57
|
|
|
56
58
|
try:
|
|
@@ -358,7 +360,7 @@ class Model(object):
|
|
|
358
360
|
] = attribute_transformer,
|
|
359
361
|
**kwargs: Any
|
|
360
362
|
) -> JSON:
|
|
361
|
-
"""Return a dict that can be
|
|
363
|
+
"""Return a dict that can be serialized using json.dump.
|
|
362
364
|
|
|
363
365
|
Advanced usage might optionally use a callback as parameter:
|
|
364
366
|
|
|
@@ -436,7 +438,7 @@ class Model(object):
|
|
|
436
438
|
"""
|
|
437
439
|
deserializer = Deserializer(cls._infer_class_models())
|
|
438
440
|
deserializer.key_extractors = ( # type: ignore
|
|
439
|
-
[
|
|
441
|
+
[ # type: ignore
|
|
440
442
|
attribute_key_case_insensitive_extractor,
|
|
441
443
|
rest_key_case_insensitive_extractor,
|
|
442
444
|
last_rest_key_case_insensitive_extractor,
|
|
@@ -544,7 +546,7 @@ class Serializer(object):
|
|
|
544
546
|
"multiple": lambda x, y: x % y != 0,
|
|
545
547
|
}
|
|
546
548
|
|
|
547
|
-
def __init__(self, classes=None):
|
|
549
|
+
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
|
|
548
550
|
self.serialize_type = {
|
|
549
551
|
"iso-8601": Serializer.serialize_iso,
|
|
550
552
|
"rfc-1123": Serializer.serialize_rfc,
|
|
@@ -560,7 +562,7 @@ class Serializer(object):
|
|
|
560
562
|
"[]": self.serialize_iter,
|
|
561
563
|
"{}": self.serialize_dict,
|
|
562
564
|
}
|
|
563
|
-
self.dependencies = dict(classes) if classes else {}
|
|
565
|
+
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
|
|
564
566
|
self.key_transformer = full_restapi_key_transformer
|
|
565
567
|
self.client_side_validation = True
|
|
566
568
|
|
|
@@ -652,8 +654,7 @@ class Serializer(object):
|
|
|
652
654
|
serialized.append(local_node) # type: ignore
|
|
653
655
|
else: # JSON
|
|
654
656
|
for k in reversed(keys): # type: ignore
|
|
655
|
-
|
|
656
|
-
new_attr = unflattened
|
|
657
|
+
new_attr = {k: new_attr}
|
|
657
658
|
|
|
658
659
|
_new_attr = new_attr
|
|
659
660
|
_serialized = serialized
|
|
@@ -682,8 +683,8 @@ class Serializer(object):
|
|
|
682
683
|
"""
|
|
683
684
|
|
|
684
685
|
# Just in case this is a dict
|
|
685
|
-
|
|
686
|
-
internal_data_type = self.dependencies.get(
|
|
686
|
+
internal_data_type_str = data_type.strip("[]{}")
|
|
687
|
+
internal_data_type = self.dependencies.get(internal_data_type_str, None)
|
|
687
688
|
try:
|
|
688
689
|
is_xml_model_serialization = kwargs["is_xml"]
|
|
689
690
|
except KeyError:
|
|
@@ -1187,7 +1188,8 @@ def rest_key_extractor(attr, attr_desc, data):
|
|
|
1187
1188
|
working_data = data
|
|
1188
1189
|
|
|
1189
1190
|
while "." in key:
|
|
1190
|
-
|
|
1191
|
+
# Need the cast, as for some reasons "split" is typed as list[str | Any]
|
|
1192
|
+
dict_keys = cast(List[str], _FLATTEN.split(key))
|
|
1191
1193
|
if len(dict_keys) == 1:
|
|
1192
1194
|
key = _decode_attribute_map_key(dict_keys[0])
|
|
1193
1195
|
break
|
|
@@ -1358,7 +1360,7 @@ class Deserializer(object):
|
|
|
1358
1360
|
|
|
1359
1361
|
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}]?")
|
|
1360
1362
|
|
|
1361
|
-
def __init__(self, classes=None):
|
|
1363
|
+
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
|
|
1362
1364
|
self.deserialize_type = {
|
|
1363
1365
|
"iso-8601": Deserializer.deserialize_iso,
|
|
1364
1366
|
"rfc-1123": Deserializer.deserialize_rfc,
|
|
@@ -1378,7 +1380,7 @@ class Deserializer(object):
|
|
|
1378
1380
|
"duration": (isodate.Duration, datetime.timedelta),
|
|
1379
1381
|
"iso-8601": (datetime.datetime),
|
|
1380
1382
|
}
|
|
1381
|
-
self.dependencies = dict(classes) if classes else {}
|
|
1383
|
+
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
|
|
1382
1384
|
self.key_extractors = [rest_key_extractor, xml_key_extractor]
|
|
1383
1385
|
# Additional properties only works if the "rest_key_extractor" is used to
|
|
1384
1386
|
# extract the keys. Making it to work whatever the key extractor is too much
|
|
@@ -1497,7 +1499,7 @@ class Deserializer(object):
|
|
|
1497
1499
|
Once classification has been determined, initialize object.
|
|
1498
1500
|
|
|
1499
1501
|
:param str target: The target object type to deserialize to.
|
|
1500
|
-
:param str/dict data: The response data to
|
|
1502
|
+
:param str/dict data: The response data to deserialize.
|
|
1501
1503
|
"""
|
|
1502
1504
|
if target is None:
|
|
1503
1505
|
return None, None
|
|
@@ -1512,7 +1514,7 @@ class Deserializer(object):
|
|
|
1512
1514
|
target = target._classify(data, self.dependencies)
|
|
1513
1515
|
except AttributeError:
|
|
1514
1516
|
pass # Target is not a Model, no classify
|
|
1515
|
-
return target, target.__class__.__name__
|
|
1517
|
+
return target, target.__class__.__name__ # type: ignore
|
|
1516
1518
|
|
|
1517
1519
|
def failsafe_deserialize(self, target_obj, data, content_type=None):
|
|
1518
1520
|
"""Ignores any errors encountered in deserialization,
|
|
@@ -1522,7 +1524,7 @@ class Deserializer(object):
|
|
|
1522
1524
|
a deserialization error.
|
|
1523
1525
|
|
|
1524
1526
|
:param str target_obj: The target object type to deserialize to.
|
|
1525
|
-
:param str/dict data: The response data to
|
|
1527
|
+
:param str/dict data: The response data to deserialize.
|
|
1526
1528
|
:param str content_type: Swagger "produces" if available.
|
|
1527
1529
|
"""
|
|
1528
1530
|
try:
|
|
@@ -19,14 +19,15 @@ def _format_url_section(template, **kwargs):
|
|
|
19
19
|
try:
|
|
20
20
|
return template.format(**kwargs)
|
|
21
21
|
except KeyError as key:
|
|
22
|
-
|
|
22
|
+
# Need the cast, as for some reasons "split" is typed as list[str | Any]
|
|
23
|
+
formatted_components = cast(List[str], template.split("/"))
|
|
23
24
|
components = [
|
|
24
25
|
c for c in formatted_components if "{{{}}}".format(key.args[0]) not in c
|
|
25
26
|
]
|
|
26
27
|
template = "/".join(components)
|
|
27
28
|
{% endif %}
|
|
28
29
|
{% if code_model.need_mixin_abc %}
|
|
29
|
-
{% for client in
|
|
30
|
+
{% for client in clients | selectattr("has_mixin") %}
|
|
30
31
|
|
|
31
32
|
class {{ client.name }}MixinABC(ABC):
|
|
32
33
|
"""DO NOT use this class. It is for internal typing use only."""
|
|
@@ -26,17 +26,17 @@ class LocalAutorestAPI(AutorestAPI):
|
|
|
26
26
|
reachable_files = []
|
|
27
27
|
self._reachable_files = reachable_files
|
|
28
28
|
self._output_folder = Path(output_folder)
|
|
29
|
-
self.values: Dict[str, Optional[str]] =
|
|
29
|
+
self.values: Dict[str, Optional[str]] = {}
|
|
30
30
|
|
|
31
31
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
32
32
|
_LOGGER.debug("Writing file: %s", filename)
|
|
33
|
-
with (self._output_folder / Path(filename)).open("w") as fd:
|
|
33
|
+
with (self._output_folder / Path(filename)).open("w", encoding="utf-8") as fd:
|
|
34
34
|
fd.write(file_content)
|
|
35
35
|
_LOGGER.debug("Written file: %s", filename)
|
|
36
36
|
|
|
37
37
|
def read_file(self, filename: Union[str, Path]) -> str:
|
|
38
38
|
_LOGGER.debug("Reading file: %s", filename)
|
|
39
|
-
with (self._output_folder / Path(filename)).open("r") as fd:
|
|
39
|
+
with (self._output_folder / Path(filename)).open("r", encoding="utf-8") as fd:
|
|
40
40
|
return fd.read()
|
|
41
41
|
|
|
42
42
|
def list_inputs(self) -> List[str]:
|
|
@@ -84,15 +84,15 @@ def main() -> None:
|
|
|
84
84
|
):
|
|
85
85
|
try:
|
|
86
86
|
import ptvsd # pylint: disable=import-outside-toplevel
|
|
87
|
-
except ImportError:
|
|
87
|
+
except ImportError as exc:
|
|
88
88
|
raise SystemExit(
|
|
89
89
|
"Please pip install ptvsd in order to use VSCode debugging"
|
|
90
|
-
)
|
|
90
|
+
) from exc
|
|
91
91
|
|
|
92
92
|
# 5678 is the default attach port in the VS Code debug configurations
|
|
93
93
|
ptvsd.enable_attach(address=("localhost", 5678), redirect_output=True)
|
|
94
94
|
ptvsd.wait_for_attach()
|
|
95
|
-
breakpoint() # pylint: disable=undefined-variable
|
|
95
|
+
breakpoint() # pylint: disable=undefined-variable,forgotten-debug-statement
|
|
96
96
|
|
|
97
97
|
_LOGGER.debug("Starting JSON RPC server")
|
|
98
98
|
|
package/autorest/m2r/__init__.py
CHANGED
|
@@ -22,7 +22,7 @@ class AutorestRender(m2r2.RestRenderer):
|
|
|
22
22
|
in the description/summary.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
def inline_html(self, html: str) -> str:
|
|
25
|
+
def inline_html(self, html: str) -> str:
|
|
26
26
|
"""Do not render inline HTML with a role definition."""
|
|
27
27
|
return f":code:`{html}`"
|
|
28
28
|
|
|
@@ -423,6 +423,12 @@ class M4Reformatter(
|
|
|
423
423
|
def legacy(self) -> bool:
|
|
424
424
|
return not (self.version_tolerant or self.low_level_client)
|
|
425
425
|
|
|
426
|
+
@property
|
|
427
|
+
def only_path_and_body_parameters_positional(self) -> bool:
|
|
428
|
+
return self.version_tolerant or bool(
|
|
429
|
+
self._autorestapi.get_boolean_value("only-path-and-body-params-positional")
|
|
430
|
+
)
|
|
431
|
+
|
|
426
432
|
@property
|
|
427
433
|
def default_optional_constants_to_none(self) -> bool:
|
|
428
434
|
return bool(
|
|
@@ -952,7 +958,11 @@ class M4Reformatter(
|
|
|
952
958
|
if name == "$host":
|
|
953
959
|
# I am the non-parameterized endpoint. Modify name based off of flag
|
|
954
960
|
|
|
955
|
-
client_name =
|
|
961
|
+
client_name = (
|
|
962
|
+
"endpoint"
|
|
963
|
+
if self.only_path_and_body_parameters_positional
|
|
964
|
+
else "base_url"
|
|
965
|
+
)
|
|
956
966
|
global_parameter["language"]["default"]["description"] = "Service URL."
|
|
957
967
|
elif (
|
|
958
968
|
global_parameter.get("origin") == "modelerfour:synthesized/api-version"
|
|
@@ -1083,7 +1093,7 @@ class M4Reformatter(
|
|
|
1083
1093
|
"skipUrlEncoding": True,
|
|
1084
1094
|
"inOverload": False,
|
|
1085
1095
|
}
|
|
1086
|
-
if self.
|
|
1096
|
+
if self.only_path_and_body_parameters_positional:
|
|
1087
1097
|
parameters.append(credential)
|
|
1088
1098
|
else:
|
|
1089
1099
|
parameters.insert(0, credential)
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
from .code_model import CodeModel
|
|
7
7
|
from .imports import ImportType, FileImport, TypingSection
|
|
8
|
+
from .global_parameter import GlobalParameter
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"CodeModel",
|
|
11
12
|
"FileImport",
|
|
12
13
|
"ImportType",
|
|
13
14
|
"TypingSection",
|
|
15
|
+
"GlobalParameter",
|
|
14
16
|
]
|
|
@@ -71,6 +71,19 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
|
|
|
71
71
|
operation_groups.sort(key=lambda x: x.name)
|
|
72
72
|
return operation_groups
|
|
73
73
|
|
|
74
|
+
@property
|
|
75
|
+
def host_variable_name(self) -> str:
|
|
76
|
+
if self.client.parameterized_host_template_to_api_version:
|
|
77
|
+
return "base_url"
|
|
78
|
+
params = (
|
|
79
|
+
self.global_parameters.parameters
|
|
80
|
+
+ self.global_parameters.service_client_specific_global_parameters
|
|
81
|
+
)
|
|
82
|
+
try:
|
|
83
|
+
return next(p for p in params if p.name in ["endpoint", "base_url"]).name
|
|
84
|
+
except StopIteration:
|
|
85
|
+
return "_endpoint"
|
|
86
|
+
|
|
74
87
|
@property
|
|
75
88
|
def last_rt_list(self) -> Dict[str, str]:
|
|
76
89
|
"""Build the a mapping RT => API version if RT doesn't exist in latest detected API version.
|
|
@@ -17,6 +17,7 @@ class GlobalParameter:
|
|
|
17
17
|
self.global_parameter_metadata_sync = global_parameter_metadata_sync
|
|
18
18
|
self.global_parameter_metadata_async = global_parameter_metadata_async
|
|
19
19
|
self.required = global_parameter_metadata_sync["required"]
|
|
20
|
+
self.method_location = global_parameter_metadata_sync["method_location"]
|
|
20
21
|
|
|
21
22
|
def _global_parameter_metadata(self, async_mode: bool) -> Dict[str, Any]:
|
|
22
23
|
if async_mode:
|
|
@@ -85,7 +85,7 @@ class FileImport:
|
|
|
85
85
|
],
|
|
86
86
|
],
|
|
87
87
|
] = (
|
|
88
|
-
imports or
|
|
88
|
+
imports or {}
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
def _add_import(
|
|
@@ -123,8 +123,8 @@ class FileImport:
|
|
|
123
123
|
]
|
|
124
124
|
] = None
|
|
125
125
|
name_input = convert_list_to_tuple(name_import)
|
|
126
|
-
self._imports.setdefault(typing_section,
|
|
127
|
-
import_type,
|
|
126
|
+
self._imports.setdefault(typing_section, {}).setdefault(
|
|
127
|
+
import_type, {}
|
|
128
128
|
).setdefault(from_section, set()).add(name_input)
|
|
129
129
|
|
|
130
130
|
def add_submodule_import(
|