@typespec/http-client-python 0.4.4 → 0.5.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/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +85 -24
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/lib.d.ts +1 -0
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +1 -0
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/run-python3.d.ts +2 -0
- package/dist/emitter/run-python3.d.ts.map +1 -0
- package/dist/emitter/run-python3.js +19 -0
- package/dist/emitter/run-python3.js.map +1 -0
- package/dist/emitter/system-requirements.d.ts +17 -0
- package/dist/emitter/system-requirements.d.ts.map +1 -0
- package/dist/emitter/system-requirements.js +167 -0
- package/dist/emitter/system-requirements.js.map +1 -0
- package/emitter/src/emitter.ts +88 -23
- package/emitter/src/lib.ts +2 -0
- package/emitter/src/run-python3.ts +20 -0
- package/emitter/src/system-requirements.ts +261 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/ci/regenerate.ts +16 -4
- package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
- package/eng/scripts/setup/build.ts +16 -0
- package/eng/scripts/setup/build_pygen_wheel.py +40 -0
- package/eng/scripts/setup/install.py +9 -3
- package/eng/scripts/setup/install.ts +32 -0
- package/eng/scripts/setup/prepare.py +3 -1
- package/eng/scripts/setup/prepare.ts +11 -0
- package/eng/scripts/setup/run-python3.ts +1 -6
- package/generator/build/lib/pygen/__init__.py +107 -0
- package/generator/build/lib/pygen/_version.py +7 -0
- package/generator/build/lib/pygen/black.py +71 -0
- package/generator/build/lib/pygen/codegen/__init__.py +357 -0
- package/generator/build/lib/pygen/codegen/_utils.py +17 -0
- package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
- package/generator/build/lib/pygen/codegen/models/base.py +186 -0
- package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
- package/generator/build/lib/pygen/codegen/models/client.py +435 -0
- package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
- package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
- package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
- package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
- package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
- package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
- package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
- package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
- package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
- package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
- package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
- package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
- package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
- package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
- package/generator/build/lib/pygen/codegen/models/property.py +170 -0
- package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
- package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
- package/generator/build/lib/pygen/codegen/models/response.py +348 -0
- package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
- package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
- package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
- package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
- package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
- package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
- package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
- package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
- package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
- package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
- package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
- package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
- package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
- package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
- package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
- package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
- package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
- package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
- package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
- package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
- package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
- package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
- package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
- package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
- package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
- package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
- package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
- package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
- package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
- package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
- package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
- package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
- package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
- package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
- package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
- package/generator/build/lib/pygen/m2r.py +65 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
- package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
- package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
- package/generator/build/lib/pygen/utils.py +163 -0
- package/generator/component-detection-pip-report.json +134 -0
- package/generator/dev_requirements.txt +0 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen.egg-info/PKG-INFO +7 -4
- package/generator/pygen.egg-info/requires.txt +7 -4
- package/generator/requirements.txt +5 -10
- package/generator/setup.py +7 -4
- package/generator/test/azure/requirements.txt +1 -1
- package/generator/test/unbranded/requirements.txt +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,198 @@
|
|
|
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
|
+
import functools
|
|
7
|
+
import json
|
|
8
|
+
from typing import List, Optional, Set, Tuple, Dict, Union, Any
|
|
9
|
+
from jinja2 import Environment
|
|
10
|
+
from ..models import (
|
|
11
|
+
OperationGroup,
|
|
12
|
+
LROOperation,
|
|
13
|
+
PagingOperation,
|
|
14
|
+
TypingSection,
|
|
15
|
+
ImportType,
|
|
16
|
+
CodeModel,
|
|
17
|
+
)
|
|
18
|
+
from .builder_serializer import get_operation_serializer
|
|
19
|
+
from .import_serializer import FileImportSerializer
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _to_string(data: Union[Tuple[Any], List[Any], str]) -> str:
|
|
23
|
+
if isinstance(data, (list, tuple)):
|
|
24
|
+
return "".join([_to_string(item) for item in data])
|
|
25
|
+
return str(data)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _json_serialize_imports(
|
|
29
|
+
imports: Dict[
|
|
30
|
+
TypingSection,
|
|
31
|
+
Dict[
|
|
32
|
+
ImportType,
|
|
33
|
+
Dict[
|
|
34
|
+
str,
|
|
35
|
+
Set[
|
|
36
|
+
Optional[
|
|
37
|
+
Union[
|
|
38
|
+
str,
|
|
39
|
+
Tuple[str, str],
|
|
40
|
+
Tuple[
|
|
41
|
+
str,
|
|
42
|
+
Optional[str],
|
|
43
|
+
Tuple[Tuple[Tuple[int, int], str, Optional[str]]],
|
|
44
|
+
],
|
|
45
|
+
]
|
|
46
|
+
]
|
|
47
|
+
],
|
|
48
|
+
],
|
|
49
|
+
],
|
|
50
|
+
]
|
|
51
|
+
) -> str:
|
|
52
|
+
if not imports:
|
|
53
|
+
return ""
|
|
54
|
+
|
|
55
|
+
json_serialize_imports = {}
|
|
56
|
+
# need to make name_import set -> list to make the dictionary json serializable
|
|
57
|
+
# not using an OrderedDict since we're iterating through a set and the order there varies
|
|
58
|
+
# going to sort the list instead
|
|
59
|
+
|
|
60
|
+
for typing_section_key, typing_section_value in imports.items():
|
|
61
|
+
json_import_type_dictionary = {}
|
|
62
|
+
for import_type_key, import_type_value in typing_section_value.items():
|
|
63
|
+
json_package_name_dictionary = {}
|
|
64
|
+
for package_name, name_imports in import_type_value.items():
|
|
65
|
+
name_import_ordered_list = []
|
|
66
|
+
if name_imports:
|
|
67
|
+
name_import_ordered_list = list(name_imports)
|
|
68
|
+
name_import_ordered_list.sort(
|
|
69
|
+
key=lambda e: (
|
|
70
|
+
_to_string(e) # type: ignore
|
|
71
|
+
if isinstance(e, (list, tuple))
|
|
72
|
+
else e if isinstance(e, str) else ""
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
json_package_name_dictionary[package_name] = name_import_ordered_list
|
|
76
|
+
json_import_type_dictionary[import_type_key] = json_package_name_dictionary
|
|
77
|
+
json_serialize_imports[typing_section_key] = json_import_type_dictionary
|
|
78
|
+
return json.dumps(json_serialize_imports)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _mixin_imports(
|
|
82
|
+
mixin_operation_group: Optional[OperationGroup],
|
|
83
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
84
|
+
if not mixin_operation_group:
|
|
85
|
+
return None, None
|
|
86
|
+
|
|
87
|
+
sync_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=False)
|
|
88
|
+
async_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=True)
|
|
89
|
+
|
|
90
|
+
return _json_serialize_imports(sync_mixin_imports.to_dict()), _json_serialize_imports(async_mixin_imports.to_dict())
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _mixin_typing_definitions(
|
|
94
|
+
mixin_operation_group: Optional[OperationGroup],
|
|
95
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
96
|
+
if not mixin_operation_group:
|
|
97
|
+
return None, None
|
|
98
|
+
|
|
99
|
+
sync_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=False)
|
|
100
|
+
async_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=True)
|
|
101
|
+
sync_mixin_typing_definitions = FileImportSerializer(sync_mixin_imports, False).get_typing_definitions()
|
|
102
|
+
async_mixin_typing_definitions = FileImportSerializer(async_mixin_imports, True).get_typing_definitions()
|
|
103
|
+
|
|
104
|
+
return sync_mixin_typing_definitions, async_mixin_typing_definitions
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class MetadataSerializer:
|
|
108
|
+
def __init__(self, code_model: CodeModel, env: Environment) -> None:
|
|
109
|
+
self.code_model = code_model
|
|
110
|
+
self.client = self.code_model.clients[0] # we only do one client for multiapi
|
|
111
|
+
self.env = env
|
|
112
|
+
|
|
113
|
+
def _choose_api_version(self) -> Tuple[str, List[str]]:
|
|
114
|
+
chosen_version = ""
|
|
115
|
+
total_api_version_set: Set[str] = set()
|
|
116
|
+
for client in self.code_model.clients:
|
|
117
|
+
for operation_group in client.operation_groups:
|
|
118
|
+
total_api_version_set.update(operation_group.api_versions)
|
|
119
|
+
|
|
120
|
+
total_api_version_list = list(total_api_version_set)
|
|
121
|
+
total_api_version_list.sort()
|
|
122
|
+
|
|
123
|
+
# switching ' to " so json can decode the dict we end up writing to file
|
|
124
|
+
total_api_version_list = [str(api_version).replace("'", '"') for api_version in total_api_version_list]
|
|
125
|
+
if len(total_api_version_list) == 1:
|
|
126
|
+
chosen_version = total_api_version_list[0]
|
|
127
|
+
elif len(total_api_version_list) > 1:
|
|
128
|
+
module_version = self.code_model.namespace.split(".")[-1]
|
|
129
|
+
for api_version in total_api_version_list:
|
|
130
|
+
if "v{}".format(api_version.replace("-", "_")) == module_version:
|
|
131
|
+
chosen_version = api_version
|
|
132
|
+
|
|
133
|
+
return chosen_version, total_api_version_list
|
|
134
|
+
|
|
135
|
+
def serialize(self) -> str:
|
|
136
|
+
def _is_lro(operation):
|
|
137
|
+
return isinstance(operation, LROOperation)
|
|
138
|
+
|
|
139
|
+
def _is_paging(operation):
|
|
140
|
+
return isinstance(operation, PagingOperation)
|
|
141
|
+
|
|
142
|
+
mixin_operation_group: Optional[OperationGroup] = next(
|
|
143
|
+
(
|
|
144
|
+
operation_group
|
|
145
|
+
for client in self.code_model.clients
|
|
146
|
+
for operation_group in client.operation_groups
|
|
147
|
+
if operation_group.is_mixin
|
|
148
|
+
),
|
|
149
|
+
None,
|
|
150
|
+
)
|
|
151
|
+
mixin_operations = mixin_operation_group.operations if mixin_operation_group else []
|
|
152
|
+
sync_mixin_imports, async_mixin_imports = _mixin_imports(mixin_operation_group)
|
|
153
|
+
(
|
|
154
|
+
sync_mixin_typing_definitions,
|
|
155
|
+
async_mixin_typing_definitions,
|
|
156
|
+
) = _mixin_typing_definitions(mixin_operation_group)
|
|
157
|
+
|
|
158
|
+
chosen_version, total_api_version_list = self._choose_api_version()
|
|
159
|
+
|
|
160
|
+
# setting to true, because for multiapi we always generate with a version file with version 0.1.0
|
|
161
|
+
self.code_model.options["package_version"] = "0.1.0"
|
|
162
|
+
template = self.env.get_template("metadata.json.jinja2")
|
|
163
|
+
|
|
164
|
+
return template.render(
|
|
165
|
+
code_model=self.code_model,
|
|
166
|
+
chosen_version=chosen_version,
|
|
167
|
+
total_api_version_list=total_api_version_list,
|
|
168
|
+
client=self.client,
|
|
169
|
+
global_parameters=self.client.parameters,
|
|
170
|
+
mixin_operations=mixin_operations,
|
|
171
|
+
any=any,
|
|
172
|
+
is_lro=_is_lro,
|
|
173
|
+
is_paging=_is_paging,
|
|
174
|
+
str=str,
|
|
175
|
+
sync_mixin_imports=sync_mixin_imports,
|
|
176
|
+
async_mixin_imports=async_mixin_imports,
|
|
177
|
+
sync_mixin_typing_definitions=sync_mixin_typing_definitions,
|
|
178
|
+
async_mixin_typing_definitions=async_mixin_typing_definitions,
|
|
179
|
+
sync_client_imports=_json_serialize_imports(self.client.imports_for_multiapi(async_mode=False).to_dict()),
|
|
180
|
+
async_client_imports=_json_serialize_imports(self.client.imports_for_multiapi(async_mode=True).to_dict()),
|
|
181
|
+
sync_config_imports=_json_serialize_imports(
|
|
182
|
+
self.client.config.imports_for_multiapi(async_mode=False).to_dict()
|
|
183
|
+
),
|
|
184
|
+
async_config_imports=_json_serialize_imports(
|
|
185
|
+
self.client.config.imports_for_multiapi(async_mode=True).to_dict()
|
|
186
|
+
),
|
|
187
|
+
get_async_operation_serializer=functools.partial(
|
|
188
|
+
get_operation_serializer,
|
|
189
|
+
code_model=self.client.code_model,
|
|
190
|
+
async_mode=True,
|
|
191
|
+
),
|
|
192
|
+
get_sync_operation_serializer=functools.partial(
|
|
193
|
+
get_operation_serializer,
|
|
194
|
+
code_model=self.client.code_model,
|
|
195
|
+
async_mode=False,
|
|
196
|
+
),
|
|
197
|
+
has_credential=bool(self.client.credential),
|
|
198
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
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 jinja2 import Environment
|
|
7
|
+
from ..models import CodeModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ModelInitSerializer:
|
|
11
|
+
def __init__(self, code_model: CodeModel, env: Environment) -> None:
|
|
12
|
+
self.code_model = code_model
|
|
13
|
+
self.env = env
|
|
14
|
+
|
|
15
|
+
def serialize(self) -> str:
|
|
16
|
+
schemas = [s.name for s in self.code_model.public_model_types]
|
|
17
|
+
schemas.sort()
|
|
18
|
+
enums = [e.name for e in self.code_model.enums if not e.internal] if self.code_model.enums else None
|
|
19
|
+
|
|
20
|
+
if enums:
|
|
21
|
+
enums.sort()
|
|
22
|
+
|
|
23
|
+
# check to see if we have any duplicate names between enum and object schemas
|
|
24
|
+
model_enum_name_intersection = set(schemas).intersection(set(enums))
|
|
25
|
+
if model_enum_name_intersection:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"We have models and enums sharing the following names: {}".format(
|
|
28
|
+
", ".join(model_enum_name_intersection)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
template = self.env.get_template("model_init.py.jinja2")
|
|
33
|
+
return template.render(code_model=self.code_model, schemas=schemas, enums=enums)
|
|
@@ -0,0 +1,335 @@
|
|
|
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 List
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
|
|
9
|
+
from ..models import ModelType, Property, ConstantType, EnumValue
|
|
10
|
+
from ..models.imports import FileImport, TypingSection, MsrestImportType, ImportType
|
|
11
|
+
from .import_serializer import FileImportSerializer
|
|
12
|
+
from .base_serializer import BaseSerializer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _documentation_string(prop: Property, description_keyword: str, docstring_type_keyword: str) -> List[str]:
|
|
16
|
+
retval: List[str] = []
|
|
17
|
+
sphinx_prefix = f":{description_keyword} {prop.client_name}:"
|
|
18
|
+
description = prop.description(is_operation_file=False).replace("\\", "\\\\")
|
|
19
|
+
retval.append(f"{sphinx_prefix} {description}" if description else sphinx_prefix)
|
|
20
|
+
retval.append(f":{docstring_type_keyword} {prop.client_name}: {prop.type.docstring_type()}")
|
|
21
|
+
return retval
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _ModelSerializer(BaseSerializer, ABC):
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def imports(self) -> FileImport: ...
|
|
27
|
+
|
|
28
|
+
def serialize(self) -> str:
|
|
29
|
+
# Generate the models
|
|
30
|
+
template = self.env.get_template("model_container.py.jinja2")
|
|
31
|
+
return template.render(
|
|
32
|
+
code_model=self.code_model,
|
|
33
|
+
imports=FileImportSerializer(self.imports()),
|
|
34
|
+
str=str,
|
|
35
|
+
serializer=self,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def declare_model(self, model: ModelType) -> str: ...
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def escape_dot(s: str):
|
|
43
|
+
return s.replace(".", "\\\\.")
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def input_documentation_string(prop: Property) -> List[str]:
|
|
47
|
+
# building the param line of the property doc
|
|
48
|
+
return _documentation_string(prop, "keyword", "paramtype")
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def variable_documentation_string(prop: Property) -> List[str]:
|
|
52
|
+
return _documentation_string(prop, "ivar", "vartype")
|
|
53
|
+
|
|
54
|
+
def super_call(self, model: ModelType) -> List[str]:
|
|
55
|
+
return [f"super().__init__({self.properties_to_pass_to_super(model)})"]
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def initialize_discriminator_property(model: ModelType, prop: Property) -> str:
|
|
59
|
+
discriminator_value = f"'{model.discriminator_value}'" if model.discriminator_value else None
|
|
60
|
+
if not discriminator_value:
|
|
61
|
+
typing = "Optional[str]"
|
|
62
|
+
else:
|
|
63
|
+
typing = "str"
|
|
64
|
+
return f"self.{prop.client_name}: {typing} = {discriminator_value}"
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def initialize_standard_property(prop: Property):
|
|
68
|
+
if not (prop.optional or prop.client_default_value is not None):
|
|
69
|
+
return f"{prop.client_name}: {prop.type_annotation()},{prop.pylint_disable()}"
|
|
70
|
+
return (
|
|
71
|
+
f"{prop.client_name}: {prop.type_annotation()} = "
|
|
72
|
+
f"{prop.client_default_value_declaration},{prop.pylint_disable()}"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def discriminator_docstring(model: ModelType) -> str:
|
|
77
|
+
return (
|
|
78
|
+
"You probably want to use the sub-classes and not this class directly. "
|
|
79
|
+
f"Known sub-classes are: {', '.join(v.name for v in model.discriminated_subtypes.values())}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _init_line_parameters(model: ModelType):
|
|
84
|
+
return [p for p in model.properties if not p.readonly and not p.is_discriminator and not p.constant]
|
|
85
|
+
|
|
86
|
+
def init_line(self, model: ModelType) -> List[str]:
|
|
87
|
+
init_properties_declaration = []
|
|
88
|
+
init_line_parameters = self._init_line_parameters(model)
|
|
89
|
+
init_line_parameters.sort(key=lambda x: x.optional)
|
|
90
|
+
if init_line_parameters:
|
|
91
|
+
init_properties_declaration.append("*,")
|
|
92
|
+
for param in init_line_parameters:
|
|
93
|
+
init_properties_declaration.append(self.initialize_standard_property(param))
|
|
94
|
+
|
|
95
|
+
return init_properties_declaration
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def properties_to_pass_to_super(model: ModelType) -> str:
|
|
99
|
+
properties_to_pass_to_super = []
|
|
100
|
+
for parent in model.parents:
|
|
101
|
+
for prop in model.properties:
|
|
102
|
+
if prop in parent.properties and not prop.is_discriminator and not prop.constant and not prop.readonly:
|
|
103
|
+
properties_to_pass_to_super.append(f"{prop.client_name}={prop.client_name}")
|
|
104
|
+
properties_to_pass_to_super.append("**kwargs")
|
|
105
|
+
return ", ".join(properties_to_pass_to_super)
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def initialize_properties(self, model: ModelType) -> List[str]: ...
|
|
109
|
+
|
|
110
|
+
def need_init(self, model: ModelType) -> bool:
|
|
111
|
+
return (not model.internal) and bool(self.init_line(model) or model.discriminator)
|
|
112
|
+
|
|
113
|
+
def pylint_disable_items(self, model: ModelType) -> List[str]:
|
|
114
|
+
if model.flattened_property or self.initialize_properties(model):
|
|
115
|
+
return [""]
|
|
116
|
+
if any(p for p in model.properties if p.is_discriminator and model.discriminator_value):
|
|
117
|
+
return [""]
|
|
118
|
+
if model.parents and any(
|
|
119
|
+
"=" in prop for parent in model.parents for prop in self.init_line(parent) if self.need_init(parent)
|
|
120
|
+
):
|
|
121
|
+
return [""]
|
|
122
|
+
return ["useless-super-delegation"]
|
|
123
|
+
|
|
124
|
+
def pylint_disable(self, model: ModelType) -> str:
|
|
125
|
+
return " # pylint: disable=" + ", ".join(self.pylint_disable_items(model))
|
|
126
|
+
|
|
127
|
+
def global_pylint_disables(self) -> str:
|
|
128
|
+
return ""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class MsrestModelSerializer(_ModelSerializer):
|
|
132
|
+
def imports(self) -> FileImport:
|
|
133
|
+
file_import = FileImport(self.code_model)
|
|
134
|
+
file_import.add_msrest_import(
|
|
135
|
+
relative_path="..",
|
|
136
|
+
msrest_import_type=MsrestImportType.Module,
|
|
137
|
+
typing_section=TypingSection.REGULAR,
|
|
138
|
+
)
|
|
139
|
+
for model in self.code_model.model_types:
|
|
140
|
+
file_import.merge(model.imports(is_operation_file=False))
|
|
141
|
+
for param in self._init_line_parameters(model):
|
|
142
|
+
file_import.merge(param.imports())
|
|
143
|
+
|
|
144
|
+
return file_import
|
|
145
|
+
|
|
146
|
+
def declare_model(self, model: ModelType) -> str:
|
|
147
|
+
basename = (
|
|
148
|
+
"msrest.serialization.Model"
|
|
149
|
+
if self.code_model.options["client_side_validation"]
|
|
150
|
+
else "_serialization.Model"
|
|
151
|
+
)
|
|
152
|
+
if model.parents:
|
|
153
|
+
basename = ", ".join([m.name for m in model.parents])
|
|
154
|
+
return f"class {model.name}({basename}):{model.pylint_disable()}"
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def get_properties_to_initialize(model: ModelType) -> List[Property]:
|
|
158
|
+
if model.parents:
|
|
159
|
+
properties_to_initialize = list(
|
|
160
|
+
{
|
|
161
|
+
p.client_name: p
|
|
162
|
+
for bm in model.parents
|
|
163
|
+
for p in model.properties
|
|
164
|
+
if p not in bm.properties or p.is_discriminator or p.constant
|
|
165
|
+
}.values()
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
properties_to_initialize = model.properties
|
|
169
|
+
return properties_to_initialize
|
|
170
|
+
|
|
171
|
+
def initialize_properties(self, model: ModelType) -> List[str]:
|
|
172
|
+
init_args = []
|
|
173
|
+
for prop in self.get_properties_to_initialize(model):
|
|
174
|
+
if prop.is_discriminator:
|
|
175
|
+
init_args.append(self.initialize_discriminator_property(model, prop))
|
|
176
|
+
elif prop.readonly:
|
|
177
|
+
init_args.append(f"self.{prop.client_name} = None")
|
|
178
|
+
elif not prop.constant:
|
|
179
|
+
init_args.append(f"self.{prop.client_name} = {prop.client_name}")
|
|
180
|
+
return init_args
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def declare_property(prop: Property) -> str:
|
|
184
|
+
if prop.flattened_names:
|
|
185
|
+
attribute_key = ".".join(_ModelSerializer.escape_dot(n) for n in prop.flattened_names)
|
|
186
|
+
else:
|
|
187
|
+
attribute_key = _ModelSerializer.escape_dot(prop.wire_name)
|
|
188
|
+
if prop.type.xml_serialization_ctxt:
|
|
189
|
+
xml_metadata = f", 'xml': {{{prop.type.xml_serialization_ctxt}}}"
|
|
190
|
+
else:
|
|
191
|
+
xml_metadata = ""
|
|
192
|
+
return (
|
|
193
|
+
f'"{prop.client_name}": {{"key": "{attribute_key}",'
|
|
194
|
+
f' "type": "{prop.msrest_deserialization_key}"{xml_metadata}}},'
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class DpgModelSerializer(_ModelSerializer):
|
|
199
|
+
def super_call(self, model: ModelType) -> List[str]:
|
|
200
|
+
super_call = f"super().__init__({self.properties_to_pass_to_super(model)})"
|
|
201
|
+
if model.flattened_property:
|
|
202
|
+
return [
|
|
203
|
+
"_flattened_input = {k: kwargs.pop(k) for k in kwargs.keys() & self.__flattened_items}",
|
|
204
|
+
super_call,
|
|
205
|
+
"for k, v in _flattened_input.items():",
|
|
206
|
+
" setattr(self, k, v)",
|
|
207
|
+
]
|
|
208
|
+
return [super_call]
|
|
209
|
+
|
|
210
|
+
def imports(self) -> FileImport:
|
|
211
|
+
file_import = FileImport(self.code_model)
|
|
212
|
+
file_import.add_submodule_import(
|
|
213
|
+
"..",
|
|
214
|
+
"_model_base",
|
|
215
|
+
ImportType.LOCAL,
|
|
216
|
+
TypingSection.REGULAR,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for model in self.code_model.model_types:
|
|
220
|
+
if model.base == "json":
|
|
221
|
+
continue
|
|
222
|
+
file_import.merge(model.imports(is_operation_file=False))
|
|
223
|
+
for prop in model.properties:
|
|
224
|
+
file_import.merge(prop.imports())
|
|
225
|
+
if model.is_polymorphic:
|
|
226
|
+
file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB)
|
|
227
|
+
if not model.internal and self.init_line(model):
|
|
228
|
+
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
229
|
+
file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
|
|
230
|
+
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
231
|
+
return file_import
|
|
232
|
+
|
|
233
|
+
def declare_model(self, model: ModelType) -> str:
|
|
234
|
+
basename = "_model_base.Model"
|
|
235
|
+
if model.parents:
|
|
236
|
+
basename = ", ".join([m.name for m in model.parents])
|
|
237
|
+
if model.discriminator_value:
|
|
238
|
+
basename += f", discriminator='{model.discriminator_value}'"
|
|
239
|
+
return f"class {model.name}({basename}):{model.pylint_disable()}"
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def get_properties_to_declare(model: ModelType) -> List[Property]:
|
|
243
|
+
if model.parents:
|
|
244
|
+
parent_properties = [p for bm in model.parents for p in bm.properties]
|
|
245
|
+
properties_to_declare = [
|
|
246
|
+
p
|
|
247
|
+
for p in model.properties
|
|
248
|
+
if not any(
|
|
249
|
+
p.client_name == pp.client_name
|
|
250
|
+
and p.type_annotation() == pp.type_annotation()
|
|
251
|
+
and not p.is_base_discriminator
|
|
252
|
+
for pp in parent_properties
|
|
253
|
+
)
|
|
254
|
+
]
|
|
255
|
+
else:
|
|
256
|
+
properties_to_declare = model.properties
|
|
257
|
+
if any(p for p in properties_to_declare if p.client_name == "_"):
|
|
258
|
+
raise ValueError("We do not generate anonymous properties")
|
|
259
|
+
return properties_to_declare
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def declare_property(prop: Property) -> str:
|
|
263
|
+
args = []
|
|
264
|
+
if prop.client_name != prop.wire_name or prop.is_discriminator:
|
|
265
|
+
args.append(f'name="{prop.wire_name}"')
|
|
266
|
+
if prop.visibility:
|
|
267
|
+
v_list = ", ".join(f'"{x}"' for x in prop.visibility)
|
|
268
|
+
args.append(f"visibility=[{v_list}]")
|
|
269
|
+
if prop.client_default_value is not None:
|
|
270
|
+
args.append(f"default={prop.client_default_value_declaration}")
|
|
271
|
+
|
|
272
|
+
if prop.is_multipart_file_input:
|
|
273
|
+
args.append("is_multipart_file_input=True")
|
|
274
|
+
elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
|
|
275
|
+
args.append(f'format="{prop.type.encode}"') # type: ignore
|
|
276
|
+
|
|
277
|
+
if prop.xml_metadata:
|
|
278
|
+
args.append(f"xml={prop.xml_metadata}")
|
|
279
|
+
|
|
280
|
+
field = "rest_discriminator" if prop.is_discriminator else "rest_field"
|
|
281
|
+
type_ignore = (
|
|
282
|
+
" # type: ignore"
|
|
283
|
+
if prop.is_discriminator and isinstance(prop.type, (ConstantType, EnumValue)) and prop.type.value
|
|
284
|
+
else ""
|
|
285
|
+
)
|
|
286
|
+
generated_code = f'{prop.client_name}: {prop.type_annotation()} = {field}({", ".join(args)})'
|
|
287
|
+
# there is 4 spaces indentation so original line length limit 120 - 4 = 116
|
|
288
|
+
pylint_disable = (
|
|
289
|
+
" # pylint: disable=line-too-long"
|
|
290
|
+
if len(generated_code) <= 116 < (len(generated_code) + len(type_ignore))
|
|
291
|
+
else ""
|
|
292
|
+
)
|
|
293
|
+
return f"{generated_code}{type_ignore}{pylint_disable}"
|
|
294
|
+
|
|
295
|
+
def initialize_properties(self, model: ModelType) -> List[str]:
|
|
296
|
+
init_args = []
|
|
297
|
+
for prop in self.get_properties_to_declare(model):
|
|
298
|
+
if prop.constant and not prop.is_base_discriminator:
|
|
299
|
+
init_args.append(f"self.{prop.client_name}: {prop.type_annotation()} = " f"{prop.get_declaration()}")
|
|
300
|
+
return init_args
|
|
301
|
+
|
|
302
|
+
@staticmethod
|
|
303
|
+
def _init_line_parameters(model: ModelType):
|
|
304
|
+
return [
|
|
305
|
+
p
|
|
306
|
+
for p in model.properties
|
|
307
|
+
if p.is_base_discriminator or not p.is_discriminator and not p.constant and p.visibility != ["read"]
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def properties_to_pass_to_super(model: ModelType) -> str:
|
|
312
|
+
properties_to_pass_to_super = ["*args"]
|
|
313
|
+
for parent in model.parents:
|
|
314
|
+
for prop in model.properties:
|
|
315
|
+
if (
|
|
316
|
+
prop.client_name in [prop.client_name for prop in parent.properties if prop.is_base_discriminator]
|
|
317
|
+
and prop.is_discriminator
|
|
318
|
+
and not prop.constant
|
|
319
|
+
and not prop.readonly
|
|
320
|
+
):
|
|
321
|
+
properties_to_pass_to_super.append(f"{prop.client_name}={prop.get_declaration()}")
|
|
322
|
+
properties_to_pass_to_super.append("**kwargs")
|
|
323
|
+
return ", ".join(properties_to_pass_to_super)
|
|
324
|
+
|
|
325
|
+
def global_pylint_disables(self) -> str:
|
|
326
|
+
result = []
|
|
327
|
+
for model in self.code_model.model_types:
|
|
328
|
+
if self.need_init(model):
|
|
329
|
+
for item in self.pylint_disable_items(model):
|
|
330
|
+
if item:
|
|
331
|
+
result.append(item)
|
|
332
|
+
final_result = set(result)
|
|
333
|
+
if final_result:
|
|
334
|
+
return "# pylint: disable=" + ", ".join(final_result)
|
|
335
|
+
return ""
|
|
@@ -0,0 +1,89 @@
|
|
|
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 Optional, List, Union
|
|
7
|
+
import functools
|
|
8
|
+
from jinja2 import Environment
|
|
9
|
+
|
|
10
|
+
from .utils import get_all_operation_groups_recursively
|
|
11
|
+
from ..models import (
|
|
12
|
+
CodeModel,
|
|
13
|
+
OperationGroup,
|
|
14
|
+
RequestBuilder,
|
|
15
|
+
OverloadedRequestBuilder,
|
|
16
|
+
Client,
|
|
17
|
+
FileImport,
|
|
18
|
+
)
|
|
19
|
+
from .import_serializer import FileImportSerializer
|
|
20
|
+
from .builder_serializer import (
|
|
21
|
+
get_operation_serializer,
|
|
22
|
+
RequestBuilderSerializer,
|
|
23
|
+
)
|
|
24
|
+
from .base_serializer import BaseSerializer
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OperationGroupsSerializer(BaseSerializer):
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
code_model: CodeModel,
|
|
31
|
+
clients: List[Client],
|
|
32
|
+
env: Environment,
|
|
33
|
+
async_mode: bool,
|
|
34
|
+
operation_group: Optional[OperationGroup] = None,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(code_model, env)
|
|
37
|
+
self.clients = clients
|
|
38
|
+
self.async_mode = async_mode
|
|
39
|
+
self.operation_group = operation_group
|
|
40
|
+
|
|
41
|
+
def _get_request_builders(
|
|
42
|
+
self, operation_group: OperationGroup
|
|
43
|
+
) -> List[Union[OverloadedRequestBuilder, RequestBuilder]]:
|
|
44
|
+
return [
|
|
45
|
+
r
|
|
46
|
+
for client in self.clients
|
|
47
|
+
for r in client.request_builders
|
|
48
|
+
if r.client.name == operation_group.client.name
|
|
49
|
+
and r.group_name == operation_group.identify_name
|
|
50
|
+
and not r.is_overload
|
|
51
|
+
and not r.abstract
|
|
52
|
+
and not r.is_lro # lro has already initial builder
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def serialize(self) -> str:
|
|
56
|
+
if self.operation_group:
|
|
57
|
+
operation_groups = [self.operation_group]
|
|
58
|
+
else:
|
|
59
|
+
operation_groups = get_all_operation_groups_recursively(self.clients)
|
|
60
|
+
|
|
61
|
+
imports = FileImport(self.code_model)
|
|
62
|
+
for operation_group in operation_groups:
|
|
63
|
+
imports.merge(
|
|
64
|
+
operation_group.imports(
|
|
65
|
+
async_mode=self.async_mode,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
template = self.env.get_or_select_template("operation_groups_container.py.jinja2")
|
|
70
|
+
|
|
71
|
+
return template.render(
|
|
72
|
+
code_model=self.code_model,
|
|
73
|
+
operation_groups=operation_groups,
|
|
74
|
+
imports=FileImportSerializer(
|
|
75
|
+
imports,
|
|
76
|
+
async_mode=self.async_mode,
|
|
77
|
+
),
|
|
78
|
+
async_mode=self.async_mode,
|
|
79
|
+
get_operation_serializer=functools.partial(
|
|
80
|
+
get_operation_serializer,
|
|
81
|
+
code_model=self.code_model,
|
|
82
|
+
async_mode=self.async_mode,
|
|
83
|
+
),
|
|
84
|
+
request_builder_serializer=RequestBuilderSerializer(
|
|
85
|
+
self.code_model,
|
|
86
|
+
async_mode=False,
|
|
87
|
+
),
|
|
88
|
+
get_request_builders=self._get_request_builders,
|
|
89
|
+
)
|