@autorest/python 6.2.11 → 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/__init__.py +8 -2
- package/autorest/codegen/models/base.py +4 -13
- package/autorest/codegen/models/client.py +9 -11
- package/autorest/codegen/models/code_model.py +3 -3
- package/autorest/codegen/models/combined_type.py +4 -3
- 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 +89 -47
- 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 +64 -28
- 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_container.py.jinja2 +2 -2
- package/autorest/codegen/templates/model_dpg.py.jinja2 +6 -4
- package/autorest/codegen/templates/model_msrest.py.jinja2 +2 -2
- package/autorest/codegen/templates/serialization.py.jinja2 +57 -29
- 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 +13 -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 +19 -7
- 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
|
@@ -38,7 +38,22 @@ import logging
|
|
|
38
38
|
import re
|
|
39
39
|
import sys
|
|
40
40
|
import codecs
|
|
41
|
-
from typing import
|
|
41
|
+
from typing import (
|
|
42
|
+
Dict,
|
|
43
|
+
Any,
|
|
44
|
+
cast,
|
|
45
|
+
Optional,
|
|
46
|
+
Union,
|
|
47
|
+
AnyStr,
|
|
48
|
+
IO,
|
|
49
|
+
Mapping,
|
|
50
|
+
Callable,
|
|
51
|
+
TypeVar,
|
|
52
|
+
MutableMapping,
|
|
53
|
+
Type,
|
|
54
|
+
List,
|
|
55
|
+
Mapping,
|
|
56
|
+
)
|
|
42
57
|
|
|
43
58
|
try:
|
|
44
59
|
from urllib import quote # type: ignore
|
|
@@ -48,12 +63,13 @@ import xml.etree.ElementTree as ET
|
|
|
48
63
|
|
|
49
64
|
import isodate # type: ignore
|
|
50
65
|
|
|
51
|
-
from typing import Dict, Any, cast
|
|
52
|
-
|
|
53
66
|
from azure.core.exceptions import DeserializationError, SerializationError, raise_with_traceback
|
|
54
67
|
|
|
55
68
|
_BOM = codecs.BOM_UTF8.decode(encoding="utf-8")
|
|
56
69
|
|
|
70
|
+
ModelType = TypeVar("ModelType", bound="Model")
|
|
71
|
+
JSON = MutableMapping[str, Any]
|
|
72
|
+
|
|
57
73
|
|
|
58
74
|
class RawDeserializer:
|
|
59
75
|
|
|
@@ -277,8 +293,8 @@ class Model(object):
|
|
|
277
293
|
_attribute_map: Dict[str, Dict[str, Any]] = {}
|
|
278
294
|
_validation: Dict[str, Dict[str, Any]] = {}
|
|
279
295
|
|
|
280
|
-
def __init__(self, **kwargs):
|
|
281
|
-
self.additional_properties = {}
|
|
296
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
297
|
+
self.additional_properties: Dict[str, Any] = {}
|
|
282
298
|
for k in kwargs:
|
|
283
299
|
if k not in self._attribute_map:
|
|
284
300
|
_LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__)
|
|
@@ -287,25 +303,25 @@ class Model(object):
|
|
|
287
303
|
else:
|
|
288
304
|
setattr(self, k, kwargs[k])
|
|
289
305
|
|
|
290
|
-
def __eq__(self, other):
|
|
306
|
+
def __eq__(self, other: Any) -> bool:
|
|
291
307
|
"""Compare objects by comparing all attributes."""
|
|
292
308
|
if isinstance(other, self.__class__):
|
|
293
309
|
return self.__dict__ == other.__dict__
|
|
294
310
|
return False
|
|
295
311
|
|
|
296
|
-
def __ne__(self, other):
|
|
312
|
+
def __ne__(self, other: Any) -> bool:
|
|
297
313
|
"""Compare objects by comparing all attributes."""
|
|
298
314
|
return not self.__eq__(other)
|
|
299
315
|
|
|
300
|
-
def __str__(self):
|
|
316
|
+
def __str__(self) -> str:
|
|
301
317
|
return str(self.__dict__)
|
|
302
318
|
|
|
303
319
|
@classmethod
|
|
304
|
-
def enable_additional_properties_sending(cls):
|
|
320
|
+
def enable_additional_properties_sending(cls) -> None:
|
|
305
321
|
cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"}
|
|
306
322
|
|
|
307
323
|
@classmethod
|
|
308
|
-
def is_xml_model(cls):
|
|
324
|
+
def is_xml_model(cls) -> bool:
|
|
309
325
|
try:
|
|
310
326
|
cls._xml_map # type: ignore
|
|
311
327
|
except AttributeError:
|
|
@@ -322,7 +338,7 @@ class Model(object):
|
|
|
322
338
|
|
|
323
339
|
return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None))
|
|
324
340
|
|
|
325
|
-
def serialize(self, keep_readonly=False, **kwargs):
|
|
341
|
+
def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON:
|
|
326
342
|
"""Return the JSON that would be sent to azure from this model.
|
|
327
343
|
|
|
328
344
|
This is an alias to `as_dict(full_restapi_key_transformer, keep_readonly=False)`.
|
|
@@ -336,8 +352,15 @@ class Model(object):
|
|
|
336
352
|
serializer = Serializer(self._infer_class_models())
|
|
337
353
|
return serializer._serialize(self, keep_readonly=keep_readonly, **kwargs)
|
|
338
354
|
|
|
339
|
-
def as_dict(
|
|
340
|
-
|
|
355
|
+
def as_dict(
|
|
356
|
+
self,
|
|
357
|
+
keep_readonly: bool = True,
|
|
358
|
+
key_transformer: Callable[
|
|
359
|
+
[str, Dict[str, Any], Any], Any
|
|
360
|
+
] = attribute_transformer,
|
|
361
|
+
**kwargs: Any
|
|
362
|
+
) -> JSON:
|
|
363
|
+
"""Return a dict that can be serialized using json.dump.
|
|
341
364
|
|
|
342
365
|
Advanced usage might optionally use a callback as parameter:
|
|
343
366
|
|
|
@@ -384,7 +407,7 @@ class Model(object):
|
|
|
384
407
|
return client_models
|
|
385
408
|
|
|
386
409
|
@classmethod
|
|
387
|
-
def deserialize(cls, data, content_type=None):
|
|
410
|
+
def deserialize(cls: Type[ModelType], data: Any, content_type: Optional[str] = None) -> ModelType:
|
|
388
411
|
"""Parse a str using the RestAPI syntax and return a model.
|
|
389
412
|
|
|
390
413
|
:param str data: A str using RestAPI structure. JSON by default.
|
|
@@ -396,7 +419,12 @@ class Model(object):
|
|
|
396
419
|
return deserializer(cls.__name__, data, content_type=content_type)
|
|
397
420
|
|
|
398
421
|
@classmethod
|
|
399
|
-
def from_dict(
|
|
422
|
+
def from_dict(
|
|
423
|
+
cls: Type[ModelType],
|
|
424
|
+
data: Any,
|
|
425
|
+
key_extractors: Optional[Callable[[str, Dict[str, Any], Any], Any]] = None,
|
|
426
|
+
content_type: Optional[str] = None,
|
|
427
|
+
) -> ModelType:
|
|
400
428
|
"""Parse a dict using given key extractor return a model.
|
|
401
429
|
|
|
402
430
|
By default consider key
|
|
@@ -409,8 +437,8 @@ class Model(object):
|
|
|
409
437
|
:raises: DeserializationError if something went wrong
|
|
410
438
|
"""
|
|
411
439
|
deserializer = Deserializer(cls._infer_class_models())
|
|
412
|
-
deserializer.key_extractors = (
|
|
413
|
-
[
|
|
440
|
+
deserializer.key_extractors = ( # type: ignore
|
|
441
|
+
[ # type: ignore
|
|
414
442
|
attribute_key_case_insensitive_extractor,
|
|
415
443
|
rest_key_case_insensitive_extractor,
|
|
416
444
|
last_rest_key_case_insensitive_extractor,
|
|
@@ -518,7 +546,7 @@ class Serializer(object):
|
|
|
518
546
|
"multiple": lambda x, y: x % y != 0,
|
|
519
547
|
}
|
|
520
548
|
|
|
521
|
-
def __init__(self, classes=None):
|
|
549
|
+
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
|
|
522
550
|
self.serialize_type = {
|
|
523
551
|
"iso-8601": Serializer.serialize_iso,
|
|
524
552
|
"rfc-1123": Serializer.serialize_rfc,
|
|
@@ -534,7 +562,7 @@ class Serializer(object):
|
|
|
534
562
|
"[]": self.serialize_iter,
|
|
535
563
|
"{}": self.serialize_dict,
|
|
536
564
|
}
|
|
537
|
-
self.dependencies = dict(classes) if classes else {}
|
|
565
|
+
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
|
|
538
566
|
self.key_transformer = full_restapi_key_transformer
|
|
539
567
|
self.client_side_validation = True
|
|
540
568
|
|
|
@@ -626,8 +654,7 @@ class Serializer(object):
|
|
|
626
654
|
serialized.append(local_node) # type: ignore
|
|
627
655
|
else: # JSON
|
|
628
656
|
for k in reversed(keys): # type: ignore
|
|
629
|
-
|
|
630
|
-
new_attr = unflattened
|
|
657
|
+
new_attr = {k: new_attr}
|
|
631
658
|
|
|
632
659
|
_new_attr = new_attr
|
|
633
660
|
_serialized = serialized
|
|
@@ -656,8 +683,8 @@ class Serializer(object):
|
|
|
656
683
|
"""
|
|
657
684
|
|
|
658
685
|
# Just in case this is a dict
|
|
659
|
-
|
|
660
|
-
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)
|
|
661
688
|
try:
|
|
662
689
|
is_xml_model_serialization = kwargs["is_xml"]
|
|
663
690
|
except KeyError:
|
|
@@ -1161,7 +1188,8 @@ def rest_key_extractor(attr, attr_desc, data):
|
|
|
1161
1188
|
working_data = data
|
|
1162
1189
|
|
|
1163
1190
|
while "." in key:
|
|
1164
|
-
|
|
1191
|
+
# Need the cast, as for some reasons "split" is typed as list[str | Any]
|
|
1192
|
+
dict_keys = cast(List[str], _FLATTEN.split(key))
|
|
1165
1193
|
if len(dict_keys) == 1:
|
|
1166
1194
|
key = _decode_attribute_map_key(dict_keys[0])
|
|
1167
1195
|
break
|
|
@@ -1332,7 +1360,7 @@ class Deserializer(object):
|
|
|
1332
1360
|
|
|
1333
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}]?")
|
|
1334
1362
|
|
|
1335
|
-
def __init__(self, classes=None):
|
|
1363
|
+
def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
|
|
1336
1364
|
self.deserialize_type = {
|
|
1337
1365
|
"iso-8601": Deserializer.deserialize_iso,
|
|
1338
1366
|
"rfc-1123": Deserializer.deserialize_rfc,
|
|
@@ -1352,7 +1380,7 @@ class Deserializer(object):
|
|
|
1352
1380
|
"duration": (isodate.Duration, datetime.timedelta),
|
|
1353
1381
|
"iso-8601": (datetime.datetime),
|
|
1354
1382
|
}
|
|
1355
|
-
self.dependencies = dict(classes) if classes else {}
|
|
1383
|
+
self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
|
|
1356
1384
|
self.key_extractors = [rest_key_extractor, xml_key_extractor]
|
|
1357
1385
|
# Additional properties only works if the "rest_key_extractor" is used to
|
|
1358
1386
|
# extract the keys. Making it to work whatever the key extractor is too much
|
|
@@ -1471,7 +1499,7 @@ class Deserializer(object):
|
|
|
1471
1499
|
Once classification has been determined, initialize object.
|
|
1472
1500
|
|
|
1473
1501
|
:param str target: The target object type to deserialize to.
|
|
1474
|
-
:param str/dict data: The response data to
|
|
1502
|
+
:param str/dict data: The response data to deserialize.
|
|
1475
1503
|
"""
|
|
1476
1504
|
if target is None:
|
|
1477
1505
|
return None, None
|
|
@@ -1486,7 +1514,7 @@ class Deserializer(object):
|
|
|
1486
1514
|
target = target._classify(data, self.dependencies)
|
|
1487
1515
|
except AttributeError:
|
|
1488
1516
|
pass # Target is not a Model, no classify
|
|
1489
|
-
return target, target.__class__.__name__
|
|
1517
|
+
return target, target.__class__.__name__ # type: ignore
|
|
1490
1518
|
|
|
1491
1519
|
def failsafe_deserialize(self, target_obj, data, content_type=None):
|
|
1492
1520
|
"""Ignores any errors encountered in deserialization,
|
|
@@ -1496,7 +1524,7 @@ class Deserializer(object):
|
|
|
1496
1524
|
a deserialization error.
|
|
1497
1525
|
|
|
1498
1526
|
:param str target_obj: The target object type to deserialize to.
|
|
1499
|
-
:param str/dict data: The response data to
|
|
1527
|
+
:param str/dict data: The response data to deserialize.
|
|
1500
1528
|
:param str content_type: Swagger "produces" if available.
|
|
1501
1529
|
"""
|
|
1502
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
|
|
|
@@ -143,6 +143,7 @@ def create_model(yaml_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
143
143
|
base["name"] = yaml_data["language"]["default"]["name"]
|
|
144
144
|
base["description"] = yaml_data["language"]["default"]["description"]
|
|
145
145
|
base["isXml"] = "xml" in yaml_data.get("serializationFormats", [])
|
|
146
|
+
base["base"] = "msrest"
|
|
146
147
|
return base
|
|
147
148
|
|
|
148
149
|
|
|
@@ -422,6 +423,12 @@ class M4Reformatter(
|
|
|
422
423
|
def legacy(self) -> bool:
|
|
423
424
|
return not (self.version_tolerant or self.low_level_client)
|
|
424
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
|
+
|
|
425
432
|
@property
|
|
426
433
|
def default_optional_constants_to_none(self) -> bool:
|
|
427
434
|
return bool(
|
|
@@ -951,7 +958,11 @@ class M4Reformatter(
|
|
|
951
958
|
if name == "$host":
|
|
952
959
|
# I am the non-parameterized endpoint. Modify name based off of flag
|
|
953
960
|
|
|
954
|
-
client_name =
|
|
961
|
+
client_name = (
|
|
962
|
+
"endpoint"
|
|
963
|
+
if self.only_path_and_body_parameters_positional
|
|
964
|
+
else "base_url"
|
|
965
|
+
)
|
|
955
966
|
global_parameter["language"]["default"]["description"] = "Service URL."
|
|
956
967
|
elif (
|
|
957
968
|
global_parameter.get("origin") == "modelerfour:synthesized/api-version"
|
|
@@ -1082,7 +1093,7 @@ class M4Reformatter(
|
|
|
1082
1093
|
"skipUrlEncoding": True,
|
|
1083
1094
|
"inOverload": False,
|
|
1084
1095
|
}
|
|
1085
|
-
if self.
|
|
1096
|
+
if self.only_path_and_body_parameters_positional:
|
|
1086
1097
|
parameters.append(credential)
|
|
1087
1098
|
else:
|
|
1088
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(
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Optional, Union
|
|
7
|
+
from typing import Any, Optional, Union, List
|
|
8
8
|
from jinja2 import PackageLoader, Environment
|
|
9
9
|
|
|
10
10
|
from .import_serializer import FileImportSerializer
|
|
11
11
|
|
|
12
12
|
from ...jsonrpc import AutorestAPI
|
|
13
|
-
from ..models import CodeModel
|
|
13
|
+
from ..models import CodeModel, GlobalParameter
|
|
14
14
|
from ... import ReaderAndWriter, ReaderAndWriterAutorest
|
|
15
15
|
|
|
16
16
|
__all__ = [
|
|
@@ -26,6 +26,15 @@ _FILE_TO_TEMPLATE = {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def _method_signature_helper(
|
|
30
|
+
parameters: List[GlobalParameter], async_mode: bool
|
|
31
|
+
) -> List[str]:
|
|
32
|
+
return [
|
|
33
|
+
p.signature(async_mode)
|
|
34
|
+
for p in sorted(parameters, key=lambda p: p.required, reverse=True)
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
29
38
|
def _get_file_path(filename: str, async_mode: bool) -> Path:
|
|
30
39
|
filename += ".py"
|
|
31
40
|
if async_mode:
|
|
@@ -48,8 +57,26 @@ class MultiAPISerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
48
57
|
def _serialize_helper(self, code_model: CodeModel, async_mode: bool) -> None:
|
|
49
58
|
def _render_template(file: str, **kwargs: Any) -> str:
|
|
50
59
|
template = self.env.get_template(_FILE_TO_TEMPLATE[file])
|
|
60
|
+
all_params = (
|
|
61
|
+
code_model.global_parameters.parameters
|
|
62
|
+
+ code_model.global_parameters.service_client_specific_global_parameters
|
|
63
|
+
)
|
|
64
|
+
positional_params = [
|
|
65
|
+
p for p in all_params if p.method_location == "positional"
|
|
66
|
+
]
|
|
67
|
+
keyword_only_params = [
|
|
68
|
+
p for p in all_params if p.method_location == "keywordOnly"
|
|
69
|
+
]
|
|
51
70
|
return template.render(
|
|
52
|
-
code_model=code_model,
|
|
71
|
+
code_model=code_model,
|
|
72
|
+
async_mode=async_mode,
|
|
73
|
+
positional_params=_method_signature_helper(
|
|
74
|
+
positional_params, async_mode
|
|
75
|
+
),
|
|
76
|
+
keyword_only_params=_method_signature_helper(
|
|
77
|
+
keyword_only_params, async_mode
|
|
78
|
+
),
|
|
79
|
+
**kwargs
|
|
53
80
|
)
|
|
54
81
|
|
|
55
82
|
# serialize init file
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
{% macro method_signature() %}
|
|
2
2
|
def __init__(
|
|
3
3
|
self,
|
|
4
|
-
{% for
|
|
5
|
-
|
|
6
|
-
{{ parameter.signature(async_mode) }}
|
|
7
|
-
{% endif %}
|
|
8
|
-
{% endfor %}
|
|
9
|
-
{% for parameter in code_model.global_parameters.parameters %}
|
|
10
|
-
{% if not parameter.required %}
|
|
11
|
-
{{ parameter.signature(async_mode) }}
|
|
12
|
-
{% endif %}
|
|
4
|
+
{% for pos in positional_params %}
|
|
5
|
+
{{ pos }}
|
|
13
6
|
{% endfor %}
|
|
14
|
-
{%
|
|
15
|
-
|
|
7
|
+
{% if keyword_only_params %}
|
|
8
|
+
*,
|
|
9
|
+
{% endif %}
|
|
10
|
+
{% for ko in keyword_only_params %}
|
|
11
|
+
{{ ko }}
|
|
16
12
|
{% endfor %}
|
|
17
13
|
**kwargs: Any
|
|
18
14
|
){{" -> None" if async_mode else "" }}:{% endmacro %}
|
|
@@ -92,7 +88,7 @@ class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_
|
|
|
92
88
|
raise ValueError("API version {} is not available".format(api_version))
|
|
93
89
|
{% endif %}
|
|
94
90
|
self._config = {{ code_model.client.name }}Configuration({{ code_model.global_parameters.call }}{{ ", " if code_model.global_parameters.call }}**kwargs)
|
|
95
|
-
self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url=
|
|
91
|
+
self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url={{ code_model.host_variable_name }}, config=self._config, **kwargs)
|
|
96
92
|
super({{ code_model.client.name }}, self).__init__(
|
|
97
93
|
api_version=api_version,
|
|
98
94
|
profile=profile
|
|
@@ -15,5 +15,7 @@ def main(namespace):
|
|
|
15
15
|
if __name__ == "__main__":
|
|
16
16
|
patched = ",".join(main(sys.argv[1]))
|
|
17
17
|
output_folder = sys.argv[2]
|
|
18
|
-
with open(
|
|
18
|
+
with open(
|
|
19
|
+
f"{output_folder}/.temp_folder/patched.txt", "w", encoding="utf-8-sig"
|
|
20
|
+
) as f:
|
|
19
21
|
f.write(patched)
|
|
@@ -48,7 +48,7 @@ def create(
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def python_run( # pylint: disable=inconsistent-return-statements
|
|
51
|
-
venv_context, module, command, directory=_ROOT_DIR
|
|
51
|
+
venv_context, module, command, directory=_ROOT_DIR
|
|
52
52
|
) -> Optional[str]:
|
|
53
53
|
try:
|
|
54
54
|
cmd_line = [
|
|
@@ -64,10 +64,11 @@ def python_run( # pylint: disable=inconsistent-return-statements
|
|
|
64
64
|
stdout=False,
|
|
65
65
|
)
|
|
66
66
|
if module == "get_all":
|
|
67
|
-
with open(
|
|
67
|
+
with open(
|
|
68
|
+
f"{command[1]}/.temp_folder/patched.txt", "r", encoding="utf-8-sig"
|
|
69
|
+
) as f:
|
|
68
70
|
return f.read()
|
|
69
71
|
except subprocess.CalledProcessError as err:
|
|
70
72
|
print(err)
|
|
71
|
-
|
|
72
|
-
sys.exit(1)
|
|
73
|
+
sys.exit(1)
|
|
73
74
|
return None
|
|
@@ -23,7 +23,6 @@ from .._utils import parse_args, get_body_type_for_description, JSON_REGEXP, KNO
|
|
|
23
23
|
def add_body_param_type(
|
|
24
24
|
code_model: Dict[str, Any],
|
|
25
25
|
body_parameter: Dict[str, Any],
|
|
26
|
-
models_mode: Optional[str],
|
|
27
26
|
):
|
|
28
27
|
if (
|
|
29
28
|
body_parameter
|
|
@@ -35,11 +34,12 @@ def add_body_param_type(
|
|
|
35
34
|
and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
|
|
36
35
|
):
|
|
37
36
|
origin_type = body_parameter["type"]["type"]
|
|
37
|
+
is_dpg_model = body_parameter["type"].get("base") == "dpg"
|
|
38
38
|
body_parameter["type"] = {
|
|
39
39
|
"type": "combined",
|
|
40
40
|
"types": [body_parameter["type"], KNOWN_TYPES["binary"]],
|
|
41
41
|
}
|
|
42
|
-
if origin_type == "model" and
|
|
42
|
+
if origin_type == "model" and is_dpg_model:
|
|
43
43
|
body_parameter["type"]["types"].insert(1, KNOWN_TYPES["any-object"])
|
|
44
44
|
code_model["types"].append(body_parameter["type"])
|
|
45
45
|
|
|
@@ -60,13 +60,21 @@ def update_overload_section(
|
|
|
60
60
|
overload_h["type"] = original_h["type"]
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def add_overload(
|
|
63
|
+
def add_overload(
|
|
64
|
+
yaml_data: Dict[str, Any], body_type: Dict[str, Any], for_flatten_params=False
|
|
65
|
+
):
|
|
64
66
|
overload = copy.deepcopy(yaml_data)
|
|
65
67
|
overload["isOverload"] = True
|
|
66
68
|
overload["bodyParameter"]["type"] = body_type
|
|
67
69
|
|
|
68
70
|
overload["overloads"] = []
|
|
69
71
|
|
|
72
|
+
if for_flatten_params:
|
|
73
|
+
overload["bodyParameter"]["flattened"] = True
|
|
74
|
+
else:
|
|
75
|
+
overload["parameters"] = [
|
|
76
|
+
p for p in overload["parameters"] if not p.get("inFlattenedBody")
|
|
77
|
+
]
|
|
70
78
|
# for yaml sync, we need to make sure all of the responses, parameters, and exceptions' types have the same yaml id
|
|
71
79
|
for overload_p, original_p in zip(overload["parameters"], yaml_data["parameters"]):
|
|
72
80
|
overload_p["type"] = original_p["type"]
|
|
@@ -107,6 +115,10 @@ def add_overloads_for_body_param(yaml_data: Dict[str, Any]) -> None:
|
|
|
107
115
|
):
|
|
108
116
|
continue
|
|
109
117
|
yaml_data["overloads"].append(add_overload(yaml_data, body_type))
|
|
118
|
+
if body_type.get("type") == "model" and body_type.get("base") == "json":
|
|
119
|
+
yaml_data["overloads"].append(
|
|
120
|
+
add_overload(yaml_data, body_type, for_flatten_params=True)
|
|
121
|
+
)
|
|
110
122
|
content_type_param = next(
|
|
111
123
|
p for p in yaml_data["parameters"] if p["restApiName"].lower() == "content-type"
|
|
112
124
|
)
|
|
@@ -162,9 +174,9 @@ def update_parameter(yaml_data: Dict[str, Any]) -> None:
|
|
|
162
174
|
if yaml_data.get("propertyToParameterName"):
|
|
163
175
|
# need to create a new one with padded keys and values
|
|
164
176
|
yaml_data["propertyToParameterName"] = {
|
|
165
|
-
pad_reserved_words(prop, PadType.PROPERTY)
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
pad_reserved_words(prop, PadType.PROPERTY): pad_reserved_words(
|
|
178
|
+
param_name, PadType.PARAMETER
|
|
179
|
+
)
|
|
168
180
|
for prop, param_name in yaml_data["propertyToParameterName"].items()
|
|
169
181
|
}
|
|
170
182
|
|
|
@@ -257,7 +269,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
257
269
|
response["discriminator"] = "operation"
|
|
258
270
|
if body_parameter and not is_overload:
|
|
259
271
|
# if we have a JSON body, we add a binary overload
|
|
260
|
-
add_body_param_type(code_model, body_parameter
|
|
272
|
+
add_body_param_type(code_model, body_parameter)
|
|
261
273
|
add_overloads_for_body_param(yaml_data)
|
|
262
274
|
|
|
263
275
|
def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
|
package/index.js
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autorest/python",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.15",
|
|
4
4
|
"description": "The Python extension for generators in AutoRest.",
|
|
5
|
+
"main": "index.js",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "https://github.com/Azure/autorest.python/tree/autorestv3"
|
package/requirements.txt
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
black==22.
|
|
1
|
+
black==22.12.0
|
|
2
2
|
click==8.1.3
|
|
3
|
-
docutils==0.
|
|
3
|
+
docutils==0.19
|
|
4
4
|
Jinja2==3.1.2
|
|
5
|
-
json-rpc==1.
|
|
6
|
-
m2r2==0.3.
|
|
5
|
+
json-rpc==1.14.0
|
|
6
|
+
m2r2==0.3.3
|
|
7
7
|
MarkupSafe==2.1.1
|
|
8
8
|
mistune==0.8.4
|
|
9
9
|
mypy-extensions==0.4.3
|
|
10
|
-
pathspec==0.
|
|
11
|
-
platformdirs==2.
|
|
10
|
+
pathspec==0.10.3
|
|
11
|
+
platformdirs==2.6.2
|
|
12
12
|
PyYAML==6.0
|
|
13
13
|
tomli==2.0.1
|