@autorest/python 5.14.0 → 5.17.0
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/ChangeLog.md +91 -2
- package/README.md +30 -4
- package/autorest/__init__.py +2 -3
- package/autorest/black/__init__.py +12 -5
- package/autorest/codegen/__init__.py +130 -179
- package/autorest/codegen/models/__init__.py +122 -78
- package/autorest/codegen/models/base_builder.py +70 -72
- package/autorest/codegen/models/base_model.py +7 -5
- package/autorest/codegen/models/{base_schema.py → base_type.py} +62 -49
- package/autorest/codegen/models/client.py +195 -36
- package/autorest/codegen/models/code_model.py +165 -299
- package/autorest/codegen/models/combined_type.py +107 -0
- package/autorest/codegen/models/constant_type.py +122 -0
- package/autorest/codegen/models/credential_types.py +224 -0
- package/autorest/codegen/models/dictionary_type.py +116 -0
- package/autorest/codegen/models/enum_type.py +195 -0
- package/autorest/codegen/models/imports.py +95 -41
- package/autorest/codegen/models/list_type.py +134 -0
- package/autorest/codegen/models/lro_operation.py +90 -133
- package/autorest/codegen/models/lro_paging_operation.py +28 -12
- package/autorest/codegen/models/model_type.py +239 -0
- package/autorest/codegen/models/operation.py +415 -241
- package/autorest/codegen/models/operation_group.py +82 -88
- package/autorest/codegen/models/paging_operation.py +101 -117
- package/autorest/codegen/models/parameter.py +307 -322
- package/autorest/codegen/models/parameter_list.py +366 -357
- package/autorest/codegen/models/primitive_types.py +544 -0
- package/autorest/codegen/models/property.py +122 -134
- package/autorest/codegen/models/request_builder.py +138 -86
- package/autorest/codegen/models/request_builder_parameter.py +122 -79
- package/autorest/codegen/models/response.py +325 -0
- package/autorest/codegen/models/utils.py +17 -1
- package/autorest/codegen/serializers/__init__.py +242 -118
- package/autorest/codegen/serializers/builder_serializer.py +863 -1027
- package/autorest/codegen/serializers/client_serializer.py +148 -82
- package/autorest/codegen/serializers/general_serializer.py +44 -47
- package/autorest/codegen/serializers/import_serializer.py +96 -31
- package/autorest/codegen/serializers/metadata_serializer.py +39 -79
- package/autorest/codegen/serializers/model_base_serializer.py +65 -29
- package/autorest/codegen/serializers/model_generic_serializer.py +9 -10
- package/autorest/codegen/serializers/model_init_serializer.py +4 -2
- package/autorest/codegen/serializers/model_python3_serializer.py +29 -22
- package/autorest/codegen/serializers/operation_groups_serializer.py +21 -18
- package/autorest/codegen/serializers/operations_init_serializer.py +23 -11
- package/autorest/codegen/serializers/parameter_serializer.py +174 -0
- package/autorest/codegen/serializers/patch_serializer.py +14 -2
- package/autorest/codegen/serializers/request_builders_serializer.py +57 -0
- package/autorest/codegen/serializers/utils.py +0 -103
- package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
- package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -7
- package/autorest/codegen/templates/config.py.jinja2 +13 -13
- package/autorest/codegen/templates/enum.py.jinja2 +4 -4
- package/autorest/codegen/templates/enum_container.py.jinja2 +1 -2
- package/autorest/codegen/templates/init.py.jinja2 +9 -6
- package/autorest/codegen/templates/keywords.jinja2 +14 -1
- package/autorest/codegen/templates/lro_operation.py.jinja2 +6 -5
- package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +6 -5
- package/autorest/codegen/templates/metadata.json.jinja2 +36 -35
- package/autorest/codegen/templates/model.py.jinja2 +23 -29
- package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
- package/autorest/codegen/templates/model_init.py.jinja2 +9 -8
- package/autorest/codegen/templates/operation.py.jinja2 +10 -15
- package/autorest/codegen/templates/operation_group.py.jinja2 +14 -13
- package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -2
- package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
- package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
- package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
- package/autorest/codegen/templates/patch.py.jinja2 +18 -29
- package/autorest/codegen/templates/request_builder.py.jinja2 +20 -13
- package/autorest/codegen/templates/setup.py.jinja2 +9 -3
- package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
- package/autorest/jsonrpc/__init__.py +7 -12
- package/autorest/jsonrpc/localapi.py +4 -3
- package/autorest/jsonrpc/server.py +28 -9
- package/autorest/jsonrpc/stdstream.py +13 -6
- package/autorest/m2r/__init__.py +5 -8
- package/autorest/m4reformatter/__init__.py +1108 -0
- package/autorest/multiapi/__init__.py +24 -14
- package/autorest/multiapi/models/client.py +21 -11
- package/autorest/multiapi/models/code_model.py +23 -10
- package/autorest/multiapi/models/config.py +4 -1
- package/autorest/multiapi/models/constant_global_parameter.py +1 -0
- package/autorest/multiapi/models/global_parameter.py +2 -1
- package/autorest/multiapi/models/global_parameters.py +14 -8
- package/autorest/multiapi/models/imports.py +35 -18
- package/autorest/multiapi/models/mixin_operation.py +5 -5
- package/autorest/multiapi/models/operation_group.py +2 -1
- package/autorest/multiapi/models/operation_mixin_group.py +21 -10
- package/autorest/multiapi/serializers/__init__.py +20 -25
- package/autorest/multiapi/serializers/import_serializer.py +47 -15
- package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
- package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
- package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
- package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
- package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
- package/autorest/multiapi/utils.py +3 -3
- package/autorest/postprocess/__init__.py +202 -0
- package/autorest/postprocess/get_all.py +19 -0
- package/autorest/postprocess/venvtools.py +73 -0
- package/autorest/preprocess/__init__.py +209 -0
- package/autorest/preprocess/helpers.py +54 -0
- package/autorest/{namer → preprocess}/python_mappings.py +25 -32
- package/package.json +3 -3
- package/run-python3.js +2 -3
- package/venvtools.py +1 -1
- package/autorest/codegen/models/constant_schema.py +0 -97
- package/autorest/codegen/models/credential_schema.py +0 -90
- package/autorest/codegen/models/credential_schema_policy.py +0 -77
- package/autorest/codegen/models/dictionary_schema.py +0 -103
- package/autorest/codegen/models/enum_schema.py +0 -246
- package/autorest/codegen/models/list_schema.py +0 -113
- package/autorest/codegen/models/object_schema.py +0 -249
- package/autorest/codegen/models/primitive_schemas.py +0 -476
- package/autorest/codegen/models/request_builder_parameter_list.py +0 -280
- package/autorest/codegen/models/rest.py +0 -42
- package/autorest/codegen/models/schema_request.py +0 -45
- package/autorest/codegen/models/schema_response.py +0 -123
- package/autorest/codegen/serializers/rest_serializer.py +0 -57
- package/autorest/namer/__init__.py +0 -25
- package/autorest/namer/name_converter.py +0 -412
|
@@ -18,12 +18,13 @@ __all__ = [
|
|
|
18
18
|
|
|
19
19
|
_FILE_TO_TEMPLATE = {
|
|
20
20
|
"init": "multiapi_init.py.jinja2",
|
|
21
|
-
"
|
|
21
|
+
"client": "multiapi_service_client.py.jinja2",
|
|
22
22
|
"config": "multiapi_config.py.jinja2",
|
|
23
23
|
"models": "multiapi_models.py.jinja2",
|
|
24
|
-
"operations_mixin": "multiapi_operations_mixin.py.jinja2"
|
|
24
|
+
"operations_mixin": "multiapi_operations_mixin.py.jinja2",
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
|
|
27
28
|
def _get_file_path(filename: str, async_mode: bool) -> Path:
|
|
28
29
|
filename += ".py"
|
|
29
30
|
if async_mode:
|
|
@@ -43,44 +44,45 @@ class MultiAPISerializer(object):
|
|
|
43
44
|
lstrip_blocks=True,
|
|
44
45
|
)
|
|
45
46
|
|
|
46
|
-
|
|
47
47
|
def _serialize_helper(self, code_model: CodeModel, async_mode: bool) -> None:
|
|
48
48
|
def _render_template(file: str, **kwargs: Any) -> str:
|
|
49
49
|
template = self.env.get_template(_FILE_TO_TEMPLATE[file])
|
|
50
|
-
return template.render(
|
|
50
|
+
return template.render(
|
|
51
|
+
code_model=code_model, async_mode=async_mode, **kwargs
|
|
52
|
+
)
|
|
51
53
|
|
|
52
54
|
# serialize init file
|
|
53
|
-
self._autorestapi.write_file(
|
|
55
|
+
self._autorestapi.write_file(
|
|
56
|
+
_get_file_path("__init__", async_mode), _render_template("init")
|
|
57
|
+
)
|
|
54
58
|
|
|
55
59
|
# serialize service client file
|
|
56
60
|
imports = FileImportSerializer(
|
|
57
|
-
code_model.
|
|
58
|
-
is_python3_file=async_mode
|
|
61
|
+
code_model.client.imports(async_mode), is_python3_file=async_mode
|
|
59
62
|
)
|
|
60
63
|
self._autorestapi.write_file(
|
|
61
|
-
_get_file_path(code_model.
|
|
62
|
-
_render_template("
|
|
64
|
+
_get_file_path(code_model.client.filename, async_mode),
|
|
65
|
+
_render_template("client", imports=imports),
|
|
63
66
|
)
|
|
64
67
|
|
|
65
68
|
# serialize config file
|
|
66
69
|
imports = FileImportSerializer(
|
|
67
|
-
code_model.config.imports(async_mode),
|
|
68
|
-
is_python3_file=async_mode
|
|
70
|
+
code_model.config.imports(async_mode), is_python3_file=async_mode
|
|
69
71
|
)
|
|
70
72
|
self._autorestapi.write_file(
|
|
71
73
|
_get_file_path("_configuration", async_mode),
|
|
72
|
-
_render_template("config", imports=imports)
|
|
74
|
+
_render_template("config", imports=imports),
|
|
73
75
|
)
|
|
74
76
|
|
|
75
77
|
# serialize mixins
|
|
76
78
|
if code_model.operation_mixin_group.mixin_operations:
|
|
77
79
|
imports = FileImportSerializer(
|
|
78
80
|
code_model.operation_mixin_group.imports(async_mode),
|
|
79
|
-
is_python3_file=async_mode
|
|
81
|
+
is_python3_file=async_mode,
|
|
80
82
|
)
|
|
81
83
|
self._autorestapi.write_file(
|
|
82
84
|
_get_file_path("_operations_mixin", async_mode),
|
|
83
|
-
_render_template("operations_mixin", imports=imports)
|
|
85
|
+
_render_template("operations_mixin", imports=imports),
|
|
84
86
|
)
|
|
85
87
|
|
|
86
88
|
# serialize models
|
|
@@ -89,21 +91,15 @@ class MultiAPISerializer(object):
|
|
|
89
91
|
def _serialize_version_file(self) -> None:
|
|
90
92
|
if self._autorestapi.read_file("_version.py"):
|
|
91
93
|
self._autorestapi.write_file(
|
|
92
|
-
"_version.py",
|
|
93
|
-
self._autorestapi.read_file("_version.py")
|
|
94
|
+
"_version.py", self._autorestapi.read_file("_version.py")
|
|
94
95
|
)
|
|
95
96
|
elif self._autorestapi.read_file("version.py"):
|
|
96
97
|
self._autorestapi.write_file(
|
|
97
|
-
"_version.py",
|
|
98
|
-
self._autorestapi.read_file("version.py")
|
|
98
|
+
"_version.py", self._autorestapi.read_file("version.py")
|
|
99
99
|
)
|
|
100
100
|
else:
|
|
101
101
|
template = self.env.get_template("multiapi_version.py.jinja2")
|
|
102
|
-
self._autorestapi.write_file(
|
|
103
|
-
Path("_version.py"),
|
|
104
|
-
template.render()
|
|
105
|
-
)
|
|
106
|
-
|
|
102
|
+
self._autorestapi.write_file(Path("_version.py"), template.render())
|
|
107
103
|
|
|
108
104
|
def serialize(self, code_model: CodeModel, no_async: Optional[bool]) -> None:
|
|
109
105
|
self._serialize_helper(code_model, async_mode=False)
|
|
@@ -115,8 +111,7 @@ class MultiAPISerializer(object):
|
|
|
115
111
|
# don't erase patch file
|
|
116
112
|
if self._autorestapi.read_file("_patch.py"):
|
|
117
113
|
self._autorestapi.write_file(
|
|
118
|
-
"_patch.py",
|
|
119
|
-
self._autorestapi.read_file("_patch.py")
|
|
114
|
+
"_patch.py", self._autorestapi.read_file("_patch.py")
|
|
120
115
|
)
|
|
121
116
|
|
|
122
117
|
self._autorestapi.write_file(Path("py.typed"), "# Marker file for PEP 561.")
|
|
@@ -7,19 +7,34 @@ from copy import deepcopy
|
|
|
7
7
|
from typing import Dict, Set, Optional, List
|
|
8
8
|
from ..models import ImportType, FileImport, TypingSection
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
def _serialize_package(
|
|
12
|
+
package_name: str, module_list: Set[Optional[str]], delimiter: str
|
|
13
|
+
) -> str:
|
|
11
14
|
buffer = []
|
|
12
15
|
if None in module_list:
|
|
13
16
|
buffer.append(f"import {package_name}")
|
|
14
17
|
if module_list != {None}:
|
|
15
18
|
buffer.append(
|
|
16
19
|
"from {} import {}".format(
|
|
17
|
-
package_name,
|
|
20
|
+
package_name,
|
|
21
|
+
", ".join(
|
|
22
|
+
sorted(
|
|
23
|
+
[
|
|
24
|
+
mod if isinstance(mod, str) else f"{mod[0]} as {mod[1]}"
|
|
25
|
+
for mod in module_list
|
|
26
|
+
if mod is not None
|
|
27
|
+
]
|
|
28
|
+
)
|
|
29
|
+
),
|
|
18
30
|
)
|
|
19
31
|
)
|
|
20
32
|
return delimiter.join(buffer)
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
|
|
35
|
+
def _serialize_type(
|
|
36
|
+
import_type_dict: Dict[str, Set[Optional[str]]], delimiter: str
|
|
37
|
+
) -> str:
|
|
23
38
|
"""Serialize a given import type."""
|
|
24
39
|
import_list = []
|
|
25
40
|
for package_name in sorted(list(import_type_dict.keys())):
|
|
@@ -27,7 +42,10 @@ def _serialize_type(import_type_dict: Dict[str, Set[Optional[str]]], delimiter:
|
|
|
27
42
|
import_list.append(_serialize_package(package_name, module_list, delimiter))
|
|
28
43
|
return delimiter.join(import_list)
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
|
|
46
|
+
def _get_import_clauses(
|
|
47
|
+
imports: Dict[ImportType, Dict[str, Set[Optional[str]]]], delimiter: str
|
|
48
|
+
) -> List[str]:
|
|
31
49
|
import_clause = []
|
|
32
50
|
for import_type in ImportType:
|
|
33
51
|
if import_type in imports:
|
|
@@ -42,33 +60,44 @@ class FileImportSerializer:
|
|
|
42
60
|
|
|
43
61
|
def _switch_typing_section_key(self, new_key: TypingSection):
|
|
44
62
|
switched_dictionary = {}
|
|
45
|
-
switched_dictionary[new_key] = self._file_import.imports[
|
|
63
|
+
switched_dictionary[new_key] = self._file_import.imports[
|
|
64
|
+
TypingSection.CONDITIONAL
|
|
65
|
+
]
|
|
46
66
|
return switched_dictionary
|
|
47
67
|
|
|
48
|
-
def _get_imports_dict(
|
|
68
|
+
def _get_imports_dict(
|
|
69
|
+
self, baseline_typing_section: TypingSection, add_conditional_typing: bool
|
|
70
|
+
):
|
|
49
71
|
# If this is a python 3 file, our regular imports include the CONDITIONAL category
|
|
50
72
|
# If this is not a python 3 file, our typing imports include the CONDITIONAL category
|
|
51
73
|
file_import_copy = deepcopy(self._file_import)
|
|
52
|
-
if add_conditional_typing and self._file_import.imports.get(
|
|
74
|
+
if add_conditional_typing and self._file_import.imports.get(
|
|
75
|
+
TypingSection.CONDITIONAL
|
|
76
|
+
):
|
|
53
77
|
# we switch the TypingSection key for the CONDITIONAL typing imports so we can merge
|
|
54
78
|
# the imports together
|
|
55
|
-
switched_imports_dictionary = self._switch_typing_section_key(
|
|
79
|
+
switched_imports_dictionary = self._switch_typing_section_key(
|
|
80
|
+
baseline_typing_section
|
|
81
|
+
)
|
|
56
82
|
switched_imports = FileImport(switched_imports_dictionary)
|
|
57
83
|
file_import_copy.merge(switched_imports)
|
|
58
84
|
return file_import_copy.imports.get(baseline_typing_section, {})
|
|
59
85
|
|
|
60
86
|
def _add_type_checking_import(self):
|
|
61
|
-
if (
|
|
62
|
-
self.
|
|
63
|
-
|
|
87
|
+
if self._file_import.imports.get(TypingSection.TYPING) or (
|
|
88
|
+
not self.is_python3_file
|
|
89
|
+
and self._file_import.imports.get(TypingSection.CONDITIONAL)
|
|
64
90
|
):
|
|
65
|
-
self._file_import.add_submodule_import(
|
|
91
|
+
self._file_import.add_submodule_import(
|
|
92
|
+
"typing", "TYPE_CHECKING", ImportType.STDLIB
|
|
93
|
+
)
|
|
66
94
|
|
|
67
95
|
def __str__(self) -> str:
|
|
68
96
|
self._add_type_checking_import()
|
|
69
97
|
regular_imports = ""
|
|
70
98
|
regular_imports_dict = self._get_imports_dict(
|
|
71
|
-
baseline_typing_section=TypingSection.REGULAR,
|
|
99
|
+
baseline_typing_section=TypingSection.REGULAR,
|
|
100
|
+
add_conditional_typing=self.is_python3_file,
|
|
72
101
|
)
|
|
73
102
|
|
|
74
103
|
if regular_imports_dict:
|
|
@@ -78,10 +107,13 @@ class FileImportSerializer:
|
|
|
78
107
|
|
|
79
108
|
typing_imports = ""
|
|
80
109
|
typing_imports_dict = self._get_imports_dict(
|
|
81
|
-
baseline_typing_section=TypingSection.TYPING,
|
|
110
|
+
baseline_typing_section=TypingSection.TYPING,
|
|
111
|
+
add_conditional_typing=not self.is_python3_file,
|
|
82
112
|
)
|
|
83
113
|
if typing_imports_dict:
|
|
84
114
|
typing_imports += "\n\nif TYPE_CHECKING:\n # pylint: disable=unused-import,ungrouped-imports\n "
|
|
85
|
-
typing_imports += "\n\n ".join(
|
|
115
|
+
typing_imports += "\n\n ".join(
|
|
116
|
+
_get_import_clauses(typing_imports_dict, "\n ")
|
|
117
|
+
)
|
|
86
118
|
|
|
87
119
|
return regular_imports + typing_imports
|
|
@@ -12,7 +12,11 @@ from ...jsonrpc import AutorestAPI
|
|
|
12
12
|
|
|
13
13
|
class MultiAPISerializer:
|
|
14
14
|
def __init__(
|
|
15
|
-
self,
|
|
15
|
+
self,
|
|
16
|
+
conf: Dict[str, Any],
|
|
17
|
+
async_mode: bool,
|
|
18
|
+
autorestapi: AutorestAPI,
|
|
19
|
+
service_client_filename: str,
|
|
16
20
|
):
|
|
17
21
|
self.conf = conf
|
|
18
22
|
self.async_mode = async_mode
|
|
@@ -33,64 +37,60 @@ class MultiAPISerializer:
|
|
|
33
37
|
return Path(filename)
|
|
34
38
|
|
|
35
39
|
def serialize(self):
|
|
36
|
-
self._autorestapi.write_file(
|
|
40
|
+
self._autorestapi.write_file(
|
|
41
|
+
self._get_file_path("__init__.py"), self.serialize_multiapi_init()
|
|
42
|
+
)
|
|
37
43
|
|
|
38
44
|
service_client_filename_with_py_extension = self.service_client_filename + ".py"
|
|
39
45
|
self._autorestapi.write_file(
|
|
40
46
|
self._get_file_path(service_client_filename_with_py_extension),
|
|
41
|
-
self.serialize_multiapi_client()
|
|
47
|
+
self.serialize_multiapi_client(),
|
|
42
48
|
)
|
|
43
49
|
|
|
44
50
|
configuration_filename = "_configuration.py"
|
|
45
51
|
self._autorestapi.write_file(
|
|
46
52
|
self._get_file_path(configuration_filename),
|
|
47
|
-
self.serialize_multiapi_config()
|
|
53
|
+
self.serialize_multiapi_config(),
|
|
48
54
|
)
|
|
49
55
|
|
|
50
56
|
operation_mixins_filename = "_operations_mixin.py"
|
|
51
57
|
if self.conf["mixin_operations"]:
|
|
52
58
|
self._autorestapi.write_file(
|
|
53
59
|
self._get_file_path(operation_mixins_filename),
|
|
54
|
-
self.serialize_multiapi_operation_mixins()
|
|
60
|
+
self.serialize_multiapi_operation_mixins(),
|
|
55
61
|
)
|
|
56
62
|
|
|
57
63
|
if self._autorestapi.read_file("_version.py"):
|
|
58
64
|
self._autorestapi.write_file(
|
|
59
|
-
"_version.py",
|
|
60
|
-
self._autorestapi.read_file("_version.py")
|
|
65
|
+
"_version.py", self._autorestapi.read_file("_version.py")
|
|
61
66
|
)
|
|
62
67
|
elif self._autorestapi.read_file("version.py"):
|
|
63
68
|
self._autorestapi.write_file(
|
|
64
|
-
"_version.py",
|
|
65
|
-
self._autorestapi.read_file("version.py")
|
|
69
|
+
"_version.py", self._autorestapi.read_file("version.py")
|
|
66
70
|
)
|
|
67
71
|
else:
|
|
68
72
|
self._autorestapi.write_file(
|
|
69
|
-
Path("_version.py"),
|
|
70
|
-
self.serialize_multiapi_version()
|
|
73
|
+
Path("_version.py"), self.serialize_multiapi_version()
|
|
71
74
|
)
|
|
72
75
|
|
|
73
76
|
# don't erase patch file
|
|
74
77
|
if self._autorestapi.read_file("_patch.py"):
|
|
75
78
|
self._autorestapi.write_file(
|
|
76
|
-
"_patch.py",
|
|
77
|
-
self._autorestapi.read_file("_patch.py")
|
|
79
|
+
"_patch.py", self._autorestapi.read_file("_patch.py")
|
|
78
80
|
)
|
|
79
81
|
|
|
80
82
|
self._autorestapi.write_file(
|
|
81
|
-
Path("models.py"),
|
|
82
|
-
self.serialize_multiapi_models()
|
|
83
|
+
Path("models.py"), self.serialize_multiapi_models()
|
|
83
84
|
)
|
|
84
85
|
|
|
85
86
|
self._autorestapi.write_file(Path("py.typed"), "# Marker file for PEP 561.")
|
|
86
87
|
|
|
87
|
-
|
|
88
88
|
def serialize_multiapi_init(self) -> str:
|
|
89
89
|
template = self.env.get_template("multiapi_init.py.jinja2")
|
|
90
90
|
return template.render(
|
|
91
91
|
service_client_filename=self.service_client_filename,
|
|
92
92
|
client_name=self.conf["client_name"],
|
|
93
|
-
async_mode=self.async_mode
|
|
93
|
+
async_mode=self.async_mode,
|
|
94
94
|
)
|
|
95
95
|
|
|
96
96
|
def serialize_multiapi_client(self) -> str:
|
|
@@ -28,8 +28,8 @@ def __init__(
|
|
|
28
28
|
# --------------------------------------------------------------------------
|
|
29
29
|
{{ imports }}
|
|
30
30
|
|
|
31
|
-
class {{ code_model.
|
|
32
|
-
"""Configuration for {{ code_model.
|
|
31
|
+
class {{ code_model.client.name }}Configuration(Configuration):
|
|
32
|
+
"""Configuration for {{ code_model.client.name }}.
|
|
33
33
|
|
|
34
34
|
Note that all parameters used to create this instance are saved as instance
|
|
35
35
|
attributes.
|
|
@@ -52,7 +52,7 @@ class {{ code_model.service_client.name }}Configuration(Configuration):
|
|
|
52
52
|
raise ValueError("Parameter '{{ parameter.name }}' must not be None.")
|
|
53
53
|
{% endif %}
|
|
54
54
|
{% endfor %}
|
|
55
|
-
super({{ code_model.
|
|
55
|
+
super({{ code_model.client.name }}Configuration, self).__init__(**kwargs)
|
|
56
56
|
|
|
57
57
|
{% for parameter in code_model.global_parameters.parameters %}
|
|
58
58
|
self.{{ parameter.name }} = {{ parameter.name }}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from .{{ code_model.
|
|
10
|
-
__all__ = ['{{ code_model.
|
|
9
|
+
from .{{ code_model.client.filename }} import {{ code_model.client.name }}
|
|
10
|
+
__all__ = ['{{ code_model.client.name }}']
|
|
11
11
|
{% if not async_mode %}
|
|
12
12
|
|
|
13
13
|
try:
|
|
@@ -14,7 +14,7 @@ from msrest import Serializer, Deserializer
|
|
|
14
14
|
{% endif %}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class {{ code_model.
|
|
17
|
+
class {{ code_model.client.name }}OperationsMixin(object):
|
|
18
18
|
{% for mixin_operation in code_model.operation_mixin_group.mixin_operations %}
|
|
19
19
|
|
|
20
20
|
{{ mixin_operation.signature(async_mode) | indent }} {{ mixin_operation.description(async_mode) | indent(8) }}
|
|
@@ -22,7 +22,7 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
|
|
|
22
22
|
{% for api in mixin_operation.available_apis|sort %}
|
|
23
23
|
{% set if_statement = "if" if loop.first else "elif" %}
|
|
24
24
|
{{ if_statement }} api_version == '{{ code_model.mod_to_api_version[api] }}':
|
|
25
|
-
from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.
|
|
25
|
+
from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.client.name }}OperationsMixin as OperationClass
|
|
26
26
|
{% endfor %}
|
|
27
27
|
else:
|
|
28
28
|
raise ValueError("API version {} does not have operation '{{ mixin_operation.name }}'".format(api_version))
|
|
@@ -30,9 +30,9 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
|
|
|
30
30
|
mixin_instance._client = self._client
|
|
31
31
|
mixin_instance._config = self._config
|
|
32
32
|
mixin_instance._serialize = Serializer(self._models_dict(api_version))
|
|
33
|
-
{% if not code_model.
|
|
33
|
+
{% if not code_model.client.client_side_validation %}
|
|
34
34
|
mixin_instance._serialize.client_side_validation = False
|
|
35
35
|
{% endif %}
|
|
36
36
|
mixin_instance._deserialize = Deserializer(self._models_dict(api_version))
|
|
37
|
-
return {{ "await " if mixin_operation.coroutine(async_mode) }}mixin_instance.{{ mixin_operation.name }}({{ mixin_operation.call }}
|
|
37
|
+
return {{ "await " if mixin_operation.coroutine(async_mode) }}mixin_instance.{{ mixin_operation.name }}({{ mixin_operation.call(async_mode) }})
|
|
38
38
|
{% endfor %}
|
|
@@ -43,8 +43,8 @@ class _SDKClient(object):
|
|
|
43
43
|
"""
|
|
44
44
|
pass
|
|
45
45
|
|
|
46
|
-
class {{ code_model.
|
|
47
|
-
"""{{ code_model.
|
|
46
|
+
class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_operations %}{{ code_model.client.name }}OperationsMixin, {% endif %}MultiApiClientMixin, _SDKClient):
|
|
47
|
+
"""{{ code_model.client.description }}
|
|
48
48
|
|
|
49
49
|
This ready contains multiple API versions, to help you deal with all of the Azure clouds
|
|
50
50
|
(Azure Stack, Azure Government, Azure China, etc.).
|
|
@@ -64,13 +64,13 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
64
64
|
:param {{ parameter.name }}: {{ parameter.description(async_mode) }}
|
|
65
65
|
:type {{ parameter.name }}: {{ parameter.docstring_type(async_mode) }}
|
|
66
66
|
{% endfor %}
|
|
67
|
-
{% if code_model.
|
|
67
|
+
{% if code_model.client.has_lro_operations %}
|
|
68
68
|
:keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present.
|
|
69
69
|
{% endif %}
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
72
|
DEFAULT_API_VERSION = '{{ code_model.mod_to_api_version[code_model.default_api_version] }}'
|
|
73
|
-
_PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.
|
|
73
|
+
_PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.client.name }}"
|
|
74
74
|
LATEST_PROFILE = ProfileDefinition({
|
|
75
75
|
_PROFILE_TAG: {
|
|
76
76
|
None: DEFAULT_API_VERSION,
|
|
@@ -82,8 +82,8 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
{{ method_signature()|indent }}
|
|
85
|
-
{% if not code_model.
|
|
86
|
-
{% for parameterized_host_template, api_versions in code_model.
|
|
85
|
+
{% if not code_model.client.host_value %}
|
|
86
|
+
{% for parameterized_host_template, api_versions in code_model.client.parameterized_host_template_to_api_version|dictsort %}
|
|
87
87
|
{% set if_statement = "if" if loop.first else "elif" %}
|
|
88
88
|
{{ if_statement ~ " api_version == '" ~ api_versions|join("' or api_version == '") ~ "'" }}:
|
|
89
89
|
base_url = {{ parameterized_host_template }}
|
|
@@ -91,9 +91,9 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
91
91
|
else:
|
|
92
92
|
raise ValueError("API version {} is not available".format(api_version))
|
|
93
93
|
{% endif %}
|
|
94
|
-
self._config = {{ code_model.
|
|
95
|
-
self._client = {{ async_prefix }}{{ code_model.
|
|
96
|
-
super({{ code_model.
|
|
94
|
+
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=base_url, config=self._config, **kwargs)
|
|
96
|
+
super({{ code_model.client.name }}, self).__init__(
|
|
97
97
|
api_version=api_version,
|
|
98
98
|
profile=profile
|
|
99
99
|
)
|
|
@@ -12,14 +12,14 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
12
12
|
def _sync_or_async(async_mode: bool) -> str:
|
|
13
13
|
return "async" if async_mode else "sync"
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
def _get_default_api_version_from_list(
|
|
16
17
|
mod_to_api_version: Dict[str, str],
|
|
17
18
|
api_versions_list: List[str],
|
|
18
19
|
preview_mode: bool,
|
|
19
|
-
user_specified_default_api: Optional[str]
|
|
20
|
+
user_specified_default_api: Optional[str],
|
|
20
21
|
) -> str:
|
|
21
|
-
"""Get the floating latest, from a random list of API versions.
|
|
22
|
-
"""
|
|
22
|
+
"""Get the floating latest, from a random list of API versions."""
|
|
23
23
|
|
|
24
24
|
# I need user_specified_default_api to be v2019_06_07_preview shaped if it exists, let's be smart
|
|
25
25
|
# and change it automatically so I can take both syntax as input
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
from venv import EnvBuilder
|
|
11
|
+
import black
|
|
12
|
+
from .venvtools import ExtendedEnvBuilder, python_run
|
|
13
|
+
|
|
14
|
+
from .. import Plugin
|
|
15
|
+
|
|
16
|
+
_BLACK_MODE = black.Mode()
|
|
17
|
+
_BLACK_MODE.line_length = 120
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_file(file: Path, file_content: str) -> str:
|
|
21
|
+
if not file.suffix == ".py":
|
|
22
|
+
return file_content
|
|
23
|
+
try:
|
|
24
|
+
file_content = black.format_file_contents(
|
|
25
|
+
file_content, fast=True, mode=_BLACK_MODE
|
|
26
|
+
)
|
|
27
|
+
except black.NothingChanged:
|
|
28
|
+
pass
|
|
29
|
+
return file_content
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PostProcessPlugin(Plugin):
|
|
33
|
+
def __init__(self, autorestapi):
|
|
34
|
+
super().__init__(autorestapi)
|
|
35
|
+
output_folder_uri = self._autorestapi.get_value("outputFolderUri")
|
|
36
|
+
if output_folder_uri.startswith("file:"):
|
|
37
|
+
output_folder_uri = output_folder_uri[5:]
|
|
38
|
+
if os.name == "nt" and output_folder_uri.startswith("///"):
|
|
39
|
+
output_folder_uri = output_folder_uri[3:]
|
|
40
|
+
self.output_folder = Path(output_folder_uri) # path to where the setup.py is
|
|
41
|
+
self.setup_venv()
|
|
42
|
+
|
|
43
|
+
# set up the venv
|
|
44
|
+
# base folder is where the code starts, i.e. where we
|
|
45
|
+
self.base_folder, self.namespace = self.get_namespace(self.output_folder, "")
|
|
46
|
+
|
|
47
|
+
def setup_venv(self):
|
|
48
|
+
venv_path = self.output_folder / Path(".temp_folder") / Path("temp_venv")
|
|
49
|
+
|
|
50
|
+
if venv_path.exists():
|
|
51
|
+
env_builder = EnvBuilder(with_pip=True)
|
|
52
|
+
self.venv_context = env_builder.ensure_directories(venv_path)
|
|
53
|
+
else:
|
|
54
|
+
env_builder = ExtendedEnvBuilder(with_pip=True)
|
|
55
|
+
env_builder.create(venv_path)
|
|
56
|
+
self.venv_context = env_builder.context
|
|
57
|
+
python_run(
|
|
58
|
+
self.venv_context,
|
|
59
|
+
"pip",
|
|
60
|
+
["install", "-e", str(self.output_folder)],
|
|
61
|
+
directory=self.output_folder,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def get_namespace(self, dir: Path, namespace: str) -> Tuple[Path, str]:
|
|
65
|
+
try:
|
|
66
|
+
init_file = next(d for d in dir.iterdir() if d.name == "__init__.py")
|
|
67
|
+
# we don't care about pkgutil inits, we skip over them
|
|
68
|
+
file_content = self._autorestapi.read_file(
|
|
69
|
+
init_file.relative_to(self.output_folder)
|
|
70
|
+
)
|
|
71
|
+
if not "pkgutil" in file_content:
|
|
72
|
+
return dir, namespace
|
|
73
|
+
except StopIteration:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
# first, see if we can get a folder that has the same name as the current output folder
|
|
78
|
+
start = self.output_folder.stem.split("-")[0]
|
|
79
|
+
next_dir = next(d for d in dir.iterdir() if d.is_dir() and d.name == start)
|
|
80
|
+
except StopIteration:
|
|
81
|
+
invalid_start_chars = [".", "_"]
|
|
82
|
+
invalid_dirs = [
|
|
83
|
+
"swagger",
|
|
84
|
+
"out",
|
|
85
|
+
"tests",
|
|
86
|
+
"samples",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
next_dir = next(
|
|
90
|
+
d
|
|
91
|
+
for d in dir.iterdir()
|
|
92
|
+
if d.is_dir()
|
|
93
|
+
and not str(d).endswith("egg-info")
|
|
94
|
+
and d.name[0] not in invalid_start_chars
|
|
95
|
+
and d.name not in invalid_dirs
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
namespace = f"{namespace}.{next_dir.name}" if namespace else next_dir.name
|
|
99
|
+
return self.get_namespace(next_dir, namespace)
|
|
100
|
+
|
|
101
|
+
def process(self) -> bool:
|
|
102
|
+
folders = [
|
|
103
|
+
f
|
|
104
|
+
for f in self.base_folder.glob("**/*")
|
|
105
|
+
if f.is_dir() and not f.stem.startswith("__")
|
|
106
|
+
]
|
|
107
|
+
# will always have the root
|
|
108
|
+
self.fix_imports_in_init(
|
|
109
|
+
generated_file_name="_client",
|
|
110
|
+
folder_path=self.base_folder,
|
|
111
|
+
namespace=self.namespace,
|
|
112
|
+
)
|
|
113
|
+
try:
|
|
114
|
+
aio_folder = next(f for f in folders if f.stem == "aio")
|
|
115
|
+
self.fix_imports_in_init(
|
|
116
|
+
generated_file_name="_client",
|
|
117
|
+
folder_path=aio_folder,
|
|
118
|
+
namespace=f"{self.namespace}.aio",
|
|
119
|
+
)
|
|
120
|
+
except StopIteration:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
models_folder = next(f for f in folders if f.stem == "models")
|
|
125
|
+
self.fix_imports_in_init(
|
|
126
|
+
generated_file_name="_models",
|
|
127
|
+
folder_path=models_folder,
|
|
128
|
+
namespace=f"{self.namespace}.models",
|
|
129
|
+
)
|
|
130
|
+
except StopIteration:
|
|
131
|
+
pass
|
|
132
|
+
operations_folders = [
|
|
133
|
+
f for f in folders if f.stem in ["operations", "_operations"]
|
|
134
|
+
]
|
|
135
|
+
for operations_folder in operations_folders:
|
|
136
|
+
aio = ".aio" if operations_folder.parent.stem == "aio" else ""
|
|
137
|
+
self.fix_imports_in_init(
|
|
138
|
+
generated_file_name="_operations",
|
|
139
|
+
folder_path=operations_folder,
|
|
140
|
+
namespace=f"{self.namespace}{aio}.{operations_folder.stem}",
|
|
141
|
+
)
|
|
142
|
+
shutil.rmtree(f"{str(self.output_folder)}/.temp_folder")
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
def fix_imports_in_init(
|
|
146
|
+
self, generated_file_name: str, folder_path: Path, namespace: str
|
|
147
|
+
) -> None:
|
|
148
|
+
customized_objects_str = python_run(
|
|
149
|
+
self.venv_context,
|
|
150
|
+
command=[namespace, str(self.output_folder)],
|
|
151
|
+
module="get_all",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if not customized_objects_str:
|
|
155
|
+
return
|
|
156
|
+
customized_objects = {
|
|
157
|
+
k: None for k in customized_objects_str.split(",")
|
|
158
|
+
}.keys() # filter out duplicates
|
|
159
|
+
file = (folder_path / "__init__.py").relative_to(self.output_folder)
|
|
160
|
+
file_content = self._autorestapi.read_file(file)
|
|
161
|
+
added_objs = []
|
|
162
|
+
for obj in customized_objects:
|
|
163
|
+
if f" import {obj}\n" in file_content:
|
|
164
|
+
# means we're overriding a generated model
|
|
165
|
+
file_content = file_content.replace(
|
|
166
|
+
f"from .{generated_file_name} import {obj}\n",
|
|
167
|
+
f"from ._patch import {obj}\n",
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
added_objs.append(obj)
|
|
171
|
+
file_content = file_content.replace(
|
|
172
|
+
"try:\n from ._patch import __all__ as _patch_all\n "
|
|
173
|
+
"from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import"
|
|
174
|
+
"\nexcept ImportError:\n _patch_all = []",
|
|
175
|
+
"",
|
|
176
|
+
)
|
|
177
|
+
file_content = file_content.replace(
|
|
178
|
+
"from ._patch import __all__ as _patch_all", ""
|
|
179
|
+
)
|
|
180
|
+
file_content = file_content.replace(
|
|
181
|
+
"from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import\n",
|
|
182
|
+
"",
|
|
183
|
+
)
|
|
184
|
+
file_content = file_content.replace(
|
|
185
|
+
"__all__.extend([p for p in _patch_all if p not in __all__])", ""
|
|
186
|
+
)
|
|
187
|
+
if added_objs:
|
|
188
|
+
# add import
|
|
189
|
+
patch_sdk_import = "from ._patch import patch_sdk as _patch_sdk"
|
|
190
|
+
imports = "\n".join([f"from ._patch import {obj}" for obj in added_objs])
|
|
191
|
+
if imports:
|
|
192
|
+
replacement = f"{imports}\n{patch_sdk_import}"
|
|
193
|
+
else:
|
|
194
|
+
replacement = patch_sdk_import
|
|
195
|
+
file_content = file_content.replace(patch_sdk_import, replacement)
|
|
196
|
+
# add to __all__
|
|
197
|
+
added_objs_all = "\n".join([f' "{obj}",' for obj in added_objs]) + "\n"
|
|
198
|
+
file_content = file_content.replace(
|
|
199
|
+
"__all__ = [", f"__all__ = [\n{added_objs_all}", 1
|
|
200
|
+
)
|
|
201
|
+
formatted_file = format_file(file, file_content)
|
|
202
|
+
self._autorestapi.write_file(file, formatted_file)
|