@typespec/http-client-python 0.4.3 → 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.
Files changed (139) hide show
  1. package/dist/emitter/emitter.d.ts.map +1 -1
  2. package/dist/emitter/emitter.js +85 -24
  3. package/dist/emitter/emitter.js.map +1 -1
  4. package/dist/emitter/lib.d.ts +1 -0
  5. package/dist/emitter/lib.d.ts.map +1 -1
  6. package/dist/emitter/lib.js +1 -0
  7. package/dist/emitter/lib.js.map +1 -1
  8. package/dist/emitter/run-python3.d.ts +2 -0
  9. package/dist/emitter/run-python3.d.ts.map +1 -0
  10. package/dist/emitter/run-python3.js +19 -0
  11. package/dist/emitter/run-python3.js.map +1 -0
  12. package/dist/emitter/system-requirements.d.ts +17 -0
  13. package/dist/emitter/system-requirements.d.ts.map +1 -0
  14. package/dist/emitter/system-requirements.js +167 -0
  15. package/dist/emitter/system-requirements.js.map +1 -0
  16. package/emitter/src/emitter.ts +88 -23
  17. package/emitter/src/lib.ts +2 -0
  18. package/emitter/src/run-python3.ts +20 -0
  19. package/emitter/src/system-requirements.ts +261 -0
  20. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  21. package/eng/scripts/ci/regenerate.ts +16 -4
  22. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  23. package/eng/scripts/setup/build.ts +16 -0
  24. package/eng/scripts/setup/build_pygen_wheel.py +40 -0
  25. package/eng/scripts/setup/install.py +9 -3
  26. package/eng/scripts/setup/install.ts +32 -0
  27. package/eng/scripts/setup/prepare.py +3 -1
  28. package/eng/scripts/setup/prepare.ts +11 -0
  29. package/eng/scripts/setup/run-python3.ts +1 -6
  30. package/generator/build/lib/pygen/__init__.py +107 -0
  31. package/generator/build/lib/pygen/_version.py +7 -0
  32. package/generator/build/lib/pygen/black.py +71 -0
  33. package/generator/build/lib/pygen/codegen/__init__.py +357 -0
  34. package/generator/build/lib/pygen/codegen/_utils.py +17 -0
  35. package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
  36. package/generator/build/lib/pygen/codegen/models/base.py +186 -0
  37. package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
  38. package/generator/build/lib/pygen/codegen/models/client.py +435 -0
  39. package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
  40. package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
  41. package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
  42. package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
  43. package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
  44. package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
  45. package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
  46. package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
  47. package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
  48. package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
  49. package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
  50. package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
  51. package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
  52. package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
  53. package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
  54. package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
  55. package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
  56. package/generator/build/lib/pygen/codegen/models/property.py +170 -0
  57. package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
  58. package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
  59. package/generator/build/lib/pygen/codegen/models/response.py +348 -0
  60. package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
  61. package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
  62. package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
  63. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
  64. package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
  65. package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
  66. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
  67. package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
  68. package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
  69. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
  70. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
  71. package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  72. package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  73. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
  74. package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
  75. package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  76. package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
  77. package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
  78. package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
  79. package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
  80. package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
  81. package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  82. package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
  83. package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  84. package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  85. package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
  86. package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  87. package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
  88. package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
  89. package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  90. package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  91. package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
  92. package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  93. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
  94. package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
  95. package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
  96. package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
  97. package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  98. package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
  99. package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  100. package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
  101. package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
  102. package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  103. package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  104. package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  105. package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  106. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  107. package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  108. package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  109. package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  110. package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
  111. package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  112. package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  113. package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  114. package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  115. package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
  116. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
  117. package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
  118. package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  119. package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
  120. package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
  121. package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
  122. package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
  123. package/generator/build/lib/pygen/m2r.py +65 -0
  124. package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
  125. package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
  126. package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
  127. package/generator/build/lib/pygen/utils.py +163 -0
  128. package/generator/component-detection-pip-report.json +134 -0
  129. package/generator/dev_requirements.txt +0 -1
  130. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  131. package/generator/pygen/m2r.py +1 -1
  132. package/generator/pygen.egg-info/PKG-INFO +7 -4
  133. package/generator/pygen.egg-info/requires.txt +7 -4
  134. package/generator/requirements.txt +5 -10
  135. package/generator/setup.py +7 -4
  136. package/generator/test/azure/requirements.txt +2 -0
  137. package/generator/test/generic_mock_api_tests/unittests/test_m2r.py +10 -0
  138. package/generator/test/unbranded/requirements.txt +2 -0
  139. package/package.json +6 -5
@@ -0,0 +1,574 @@
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 logging
7
+ from typing import List, Optional, Any, Union
8
+ from pathlib import Path
9
+ from jinja2 import PackageLoader, Environment, FileSystemLoader, StrictUndefined
10
+
11
+ from ... import ReaderAndWriter
12
+ from ..models import (
13
+ OperationGroup,
14
+ RequestBuilder,
15
+ OverloadedRequestBuilder,
16
+ CodeModel,
17
+ Client,
18
+ )
19
+ from .enum_serializer import EnumSerializer
20
+ from .general_serializer import GeneralSerializer
21
+ from .model_init_serializer import ModelInitSerializer
22
+ from .model_serializer import DpgModelSerializer, MsrestModelSerializer
23
+ from .operations_init_serializer import OperationsInitSerializer
24
+ from .operation_groups_serializer import OperationGroupsSerializer
25
+ from .metadata_serializer import MetadataSerializer
26
+ from .request_builders_serializer import RequestBuildersSerializer
27
+ from .patch_serializer import PatchSerializer
28
+ from .sample_serializer import SampleSerializer
29
+ from .test_serializer import TestSerializer, TestGeneralSerializer
30
+ from .types_serializer import TypesSerializer
31
+ from ...utils import to_snake_case
32
+ from .._utils import VALID_PACKAGE_MODE
33
+ from .utils import (
34
+ extract_sample_name,
35
+ get_namespace_from_package_name,
36
+ get_namespace_config,
37
+ get_all_operation_groups_recursively,
38
+ )
39
+
40
+ _LOGGER = logging.getLogger(__name__)
41
+
42
+ __all__ = [
43
+ "JinjaSerializer",
44
+ ]
45
+
46
+ _PACKAGE_FILES = [
47
+ "CHANGELOG.md.jinja2",
48
+ "dev_requirements.txt.jinja2",
49
+ "LICENSE.jinja2",
50
+ "MANIFEST.in.jinja2",
51
+ "README.md.jinja2",
52
+ "setup.py.jinja2",
53
+ ]
54
+
55
+ _REGENERATE_FILES = {"setup.py", "MANIFEST.in"}
56
+
57
+
58
+ # extract sub folders. For example, source_file_path is like:
59
+ # "xxx/resource-manager/Microsoft.XX/stable/2023-04-01/examples/Compute/createOrUpdate/AKSCompute.json",
60
+ # and we want to extract the sub folders after "examples/", which is "compute/create_or_update"
61
+ def _sample_output_path(source_file_path: str) -> Path:
62
+ posix_path = Path(source_file_path).as_posix()
63
+ if "examples/" in posix_path:
64
+ after_examples = Path(posix_path.split("examples/", maxsplit=1)[-1]).parent
65
+ return Path("/".join([to_snake_case(i) for i in after_examples.parts]))
66
+ return Path("")
67
+
68
+
69
+ class JinjaSerializer(ReaderAndWriter):
70
+ def __init__(
71
+ self,
72
+ code_model: CodeModel,
73
+ *,
74
+ output_folder: Union[str, Path],
75
+ **kwargs: Any,
76
+ ) -> None:
77
+ super().__init__(output_folder=output_folder, **kwargs)
78
+ self.code_model = code_model
79
+
80
+ @property
81
+ def has_aio_folder(self) -> bool:
82
+ return not self.code_model.options["no_async"] and bool(self.code_model.has_operations)
83
+
84
+ @property
85
+ def has_operations_folder(self) -> bool:
86
+ return self.code_model.options["show_operations"] and bool(self.code_model.has_operations)
87
+
88
+ def _serialize_namespace_level(self, env: Environment, namespace_path: Path, clients: List[Client]) -> None:
89
+ # if there was a patch file before, we keep it
90
+ self._keep_patch_file(namespace_path / Path("_patch.py"), env)
91
+ if self.has_aio_folder:
92
+ self._keep_patch_file(namespace_path / Path("aio") / Path("_patch.py"), env)
93
+
94
+ if self.has_operations_folder:
95
+ self._keep_patch_file(
96
+ namespace_path / Path(self.code_model.operations_folder_name) / Path("_patch.py"),
97
+ env,
98
+ )
99
+ if self.has_aio_folder:
100
+ self._keep_patch_file(
101
+ namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path("_patch.py"),
102
+ env,
103
+ )
104
+ self._serialize_and_write_top_level_folder(env=env, namespace_path=namespace_path, clients=clients)
105
+
106
+ if any(c for c in self.code_model.clients if c.operation_groups):
107
+ if self.code_model.options["builders_visibility"] != "embedded":
108
+ self._serialize_and_write_rest_layer(env=env, namespace_path=namespace_path)
109
+ if self.has_aio_folder:
110
+ self._serialize_and_write_aio_top_level_folder(
111
+ env=env,
112
+ namespace_path=namespace_path,
113
+ clients=clients,
114
+ )
115
+
116
+ if self.has_operations_folder:
117
+ self._serialize_and_write_operations_folder(clients, env=env, namespace_path=namespace_path)
118
+ if self.code_model.options["multiapi"]:
119
+ self._serialize_and_write_metadata(env=env, namespace_path=namespace_path)
120
+ if self.code_model.options["package_mode"]:
121
+ self._serialize_and_write_package_files(namespace_path=namespace_path)
122
+
123
+ if (
124
+ self.code_model.options["show_operations"]
125
+ and self.code_model.has_operations
126
+ and self.code_model.options["generate_sample"]
127
+ ):
128
+ self._serialize_and_write_sample(env, namespace_path)
129
+
130
+ if (
131
+ self.code_model.options["show_operations"]
132
+ and self.code_model.has_operations
133
+ and self.code_model.options["generate_test"]
134
+ ):
135
+ self._serialize_and_write_test(env, namespace_path)
136
+
137
+ def serialize(self) -> None:
138
+ env = Environment(
139
+ loader=PackageLoader("pygen.codegen", "templates"),
140
+ keep_trailing_newline=True,
141
+ line_statement_prefix="##",
142
+ line_comment_prefix="###",
143
+ trim_blocks=True,
144
+ lstrip_blocks=True,
145
+ )
146
+
147
+ namespace_path = (
148
+ Path(".") if self.code_model.options["no_namespace_folders"] else Path(*self._name_space().split("."))
149
+ )
150
+
151
+ p = namespace_path.parent
152
+ general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
153
+ while p != Path("."):
154
+ # write pkgutil init file
155
+ self.write_file(
156
+ p / Path("__init__.py"),
157
+ general_serializer.serialize_pkgutil_init_file(),
158
+ )
159
+ p = p.parent
160
+
161
+ # serialize main module
162
+ self._serialize_namespace_level(
163
+ env,
164
+ namespace_path,
165
+ [c for c in self.code_model.clients if c.has_operations],
166
+ )
167
+ # serialize sub modules
168
+ for (
169
+ subnamespace,
170
+ clients,
171
+ ) in self.code_model.subnamespace_to_clients.items():
172
+ subnamespace_path = namespace_path / Path(subnamespace)
173
+ self._serialize_namespace_level(env, subnamespace_path, [c for c in clients if c.has_operations])
174
+
175
+ if self.code_model.options["models_mode"] and (self.code_model.model_types or self.code_model.enums):
176
+ self._keep_patch_file(namespace_path / Path("models") / Path("_patch.py"), env)
177
+
178
+ if self.code_model.options["models_mode"] and (self.code_model.model_types or self.code_model.enums):
179
+ self._serialize_and_write_models_folder(env=env, namespace_path=namespace_path)
180
+ if not self.code_model.options["models_mode"]:
181
+ # keep models file if users ended up just writing a models file
182
+ if self.read_file(namespace_path / Path("models.py")):
183
+ self.write_file(
184
+ namespace_path / Path("models.py"),
185
+ self.read_file(namespace_path / Path("models.py")),
186
+ )
187
+ if self.code_model.named_unions:
188
+ self.write_file(
189
+ namespace_path / Path("_types.py"),
190
+ TypesSerializer(code_model=self.code_model, env=env).serialize(),
191
+ )
192
+
193
+ def _serialize_and_write_package_files(self, namespace_path: Path) -> None:
194
+ root_of_sdk = self._package_root_folder(namespace_path)
195
+ if self.code_model.options["package_mode"] in VALID_PACKAGE_MODE:
196
+ env = Environment(
197
+ loader=PackageLoader("pygen.codegen", "templates/packaging_templates"),
198
+ undefined=StrictUndefined,
199
+ )
200
+
201
+ package_files = _PACKAGE_FILES
202
+ elif Path(self.code_model.options["package_mode"]).exists():
203
+ env = Environment(
204
+ loader=FileSystemLoader(str(Path(self.code_model.options["package_mode"]))),
205
+ keep_trailing_newline=True,
206
+ undefined=StrictUndefined,
207
+ )
208
+ package_files = env.list_templates()
209
+ else:
210
+ return
211
+ serializer = GeneralSerializer(self.code_model, env, async_mode=False)
212
+ params = self.code_model.options["packaging_files_config"] or {}
213
+ for template_name in package_files:
214
+ if not self.code_model.is_azure_flavor and template_name == "dev_requirements.txt.jinja2":
215
+ continue
216
+ file = template_name.replace(".jinja2", "")
217
+ output_name = root_of_sdk / file
218
+ if not self.read_file(output_name) or file in _REGENERATE_FILES:
219
+ self.write_file(
220
+ output_name,
221
+ serializer.serialize_package_file(template_name, **params),
222
+ )
223
+
224
+ def _keep_patch_file(self, path_file: Path, env: Environment):
225
+ if self.read_file(path_file):
226
+ self.write_file(path_file, self.read_file(path_file))
227
+ else:
228
+ self.write_file(
229
+ path_file,
230
+ PatchSerializer(env=env, code_model=self.code_model).serialize(),
231
+ )
232
+
233
+ def _serialize_and_write_models_folder(self, env: Environment, namespace_path: Path) -> None:
234
+ # Write the models folder
235
+ models_path = namespace_path / Path("models")
236
+ serializer = DpgModelSerializer if self.code_model.options["models_mode"] == "dpg" else MsrestModelSerializer
237
+ if self.code_model.model_types:
238
+ self.write_file(
239
+ models_path / Path(f"{self.code_model.models_filename}.py"),
240
+ serializer(code_model=self.code_model, env=env).serialize(),
241
+ )
242
+ if self.code_model.enums:
243
+ self.write_file(
244
+ models_path / Path(f"{self.code_model.enums_filename}.py"),
245
+ EnumSerializer(code_model=self.code_model, env=env).serialize(),
246
+ )
247
+ self.write_file(
248
+ models_path / Path("__init__.py"),
249
+ ModelInitSerializer(code_model=self.code_model, env=env).serialize(),
250
+ )
251
+
252
+ def _serialize_and_write_rest_layer(self, env: Environment, namespace_path: Path) -> None:
253
+ rest_path = namespace_path / Path(self.code_model.rest_layer_name)
254
+ group_names = {rb.group_name for c in self.code_model.clients for rb in c.request_builders}
255
+
256
+ for group_name in group_names:
257
+ request_builders = [
258
+ r for c in self.code_model.clients for r in c.request_builders if r.group_name == group_name
259
+ ]
260
+ self._serialize_and_write_single_rest_layer(env, rest_path, request_builders)
261
+ if not "" in group_names:
262
+ self.write_file(
263
+ rest_path / Path("__init__.py"),
264
+ self.code_model.options["license_header"],
265
+ )
266
+
267
+ def _serialize_and_write_single_rest_layer(
268
+ self,
269
+ env: Environment,
270
+ rest_path: Path,
271
+ request_builders: List[Union[RequestBuilder, OverloadedRequestBuilder]],
272
+ ) -> None:
273
+ group_name = request_builders[0].group_name
274
+ output_path = rest_path / Path(group_name) if group_name else rest_path
275
+ # write generic request builders file
276
+ self.write_file(
277
+ output_path / Path("_request_builders.py"),
278
+ RequestBuildersSerializer(
279
+ code_model=self.code_model,
280
+ env=env,
281
+ request_builders=request_builders,
282
+ ).serialize_request_builders(),
283
+ )
284
+
285
+ # write rest init file
286
+ self.write_file(
287
+ output_path / Path("__init__.py"),
288
+ RequestBuildersSerializer(
289
+ code_model=self.code_model,
290
+ env=env,
291
+ request_builders=request_builders,
292
+ ).serialize_init(),
293
+ )
294
+
295
+ def _serialize_and_write_operations_file(
296
+ self,
297
+ env: Environment,
298
+ clients: List[Client],
299
+ namespace_path: Path,
300
+ operation_group: Optional[OperationGroup] = None,
301
+ ) -> None:
302
+ filename = operation_group.filename if operation_group else "_operations"
303
+ # write first sync file
304
+ operation_group_serializer = OperationGroupsSerializer(
305
+ code_model=self.code_model,
306
+ clients=clients,
307
+ env=env,
308
+ async_mode=False,
309
+ operation_group=operation_group,
310
+ )
311
+ self.write_file(
312
+ namespace_path / Path(self.code_model.operations_folder_name) / Path(f"{filename}.py"),
313
+ operation_group_serializer.serialize(),
314
+ )
315
+
316
+ if self.has_aio_folder:
317
+ # write async operation group and operation files
318
+ operation_group_async_serializer = OperationGroupsSerializer(
319
+ code_model=self.code_model,
320
+ clients=clients,
321
+ env=env,
322
+ async_mode=True,
323
+ operation_group=operation_group,
324
+ )
325
+ self.write_file(
326
+ (namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path(f"{filename}.py")),
327
+ operation_group_async_serializer.serialize(),
328
+ )
329
+
330
+ def _serialize_and_write_operations_folder(
331
+ self, clients: List[Client], env: Environment, namespace_path: Path
332
+ ) -> None:
333
+ # write sync operations init file
334
+ operations_init_serializer = OperationsInitSerializer(
335
+ code_model=self.code_model, clients=clients, env=env, async_mode=False
336
+ )
337
+ self.write_file(
338
+ namespace_path / Path(self.code_model.operations_folder_name) / Path("__init__.py"),
339
+ operations_init_serializer.serialize(),
340
+ )
341
+
342
+ # write async operations init file
343
+ if self.has_aio_folder:
344
+ operations_async_init_serializer = OperationsInitSerializer(
345
+ code_model=self.code_model, clients=clients, env=env, async_mode=True
346
+ )
347
+ self.write_file(
348
+ namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path("__init__.py"),
349
+ operations_async_init_serializer.serialize(),
350
+ )
351
+
352
+ if self.code_model.options["combine_operation_files"]:
353
+ self._serialize_and_write_operations_file(
354
+ env=env,
355
+ namespace_path=namespace_path,
356
+ clients=clients,
357
+ )
358
+ else:
359
+ for operation_group in get_all_operation_groups_recursively(self.code_model.clients):
360
+ self._serialize_and_write_operations_file(
361
+ env=env,
362
+ namespace_path=namespace_path,
363
+ operation_group=operation_group,
364
+ clients=clients,
365
+ )
366
+
367
+ def _serialize_and_write_version_file(
368
+ self,
369
+ namespace_path: Path,
370
+ general_serializer: GeneralSerializer,
371
+ ):
372
+ def _read_version_file(original_version_file_name: str) -> str:
373
+ return self.read_file(namespace_path / original_version_file_name)
374
+
375
+ def _write_version_file(original_version_file_name: str) -> None:
376
+ self.write_file(
377
+ namespace_path / Path("_version.py"),
378
+ _read_version_file(original_version_file_name),
379
+ )
380
+
381
+ keep_version_file = self.code_model.options["keep_version_file"]
382
+ if keep_version_file and _read_version_file("_version.py"):
383
+ _write_version_file(original_version_file_name="_version.py")
384
+ elif keep_version_file and _read_version_file("version.py"):
385
+ _write_version_file(original_version_file_name="version.py")
386
+ elif self.code_model.options["package_version"]:
387
+ self.write_file(
388
+ namespace_path / Path("_version.py"),
389
+ general_serializer.serialize_version_file(),
390
+ )
391
+
392
+ def _serialize_client_and_config_files(
393
+ self,
394
+ namespace_path: Path,
395
+ general_serializer: GeneralSerializer,
396
+ async_mode: bool,
397
+ clients: List[Client],
398
+ ) -> None:
399
+ if self.code_model.has_operations:
400
+ namespace_path = namespace_path / Path("aio") if async_mode else namespace_path
401
+ self.write_file(
402
+ namespace_path / Path(f"{self.code_model.client_filename}.py"),
403
+ general_serializer.serialize_service_client_file(clients),
404
+ )
405
+ self.write_file(
406
+ namespace_path / Path("_configuration.py"),
407
+ general_serializer.serialize_config_file(clients),
408
+ )
409
+
410
+ def _serialize_and_write_top_level_folder(
411
+ self, env: Environment, namespace_path: Path, clients: List[Client]
412
+ ) -> None:
413
+ general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
414
+
415
+ self.write_file(
416
+ namespace_path / Path("__init__.py"),
417
+ general_serializer.serialize_init_file(clients),
418
+ )
419
+
420
+ # Write the service client
421
+ self._serialize_client_and_config_files(namespace_path, general_serializer, async_mode=False, clients=clients)
422
+ if self.code_model.need_vendored_code(async_mode=False):
423
+ self.write_file(
424
+ namespace_path / Path("_vendor.py"),
425
+ general_serializer.serialize_vendor_file(clients),
426
+ )
427
+
428
+ self._serialize_and_write_version_file(namespace_path, general_serializer)
429
+
430
+ # write the empty py.typed file
431
+ self.write_file(namespace_path / Path("py.typed"), "# Marker file for PEP 561.")
432
+
433
+ if not self.code_model.options["client_side_validation"] and not self.code_model.options["multiapi"]:
434
+ self.write_file(
435
+ namespace_path / Path("_serialization.py"),
436
+ general_serializer.serialize_serialization_file(),
437
+ )
438
+ if self.code_model.options["models_mode"] == "dpg":
439
+ self.write_file(
440
+ namespace_path / Path("_model_base.py"),
441
+ general_serializer.serialize_model_base_file(),
442
+ )
443
+
444
+ if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation):
445
+ self.write_file(
446
+ namespace_path / Path("_validation.py"),
447
+ general_serializer.serialize_validation_file(),
448
+ )
449
+ if self.code_model.options.get("emit_cross_language_definition_file"):
450
+ self.write_file(
451
+ Path("./apiview_mapping_python.json"),
452
+ general_serializer.serialize_cross_language_definition_file(),
453
+ )
454
+
455
+ # Write the setup file
456
+ if self.code_model.options["basic_setup_py"]:
457
+ self.write_file(Path("setup.py"), general_serializer.serialize_setup_file())
458
+
459
+ def _serialize_and_write_aio_top_level_folder(
460
+ self, env: Environment, namespace_path: Path, clients: List[Client]
461
+ ) -> None:
462
+ aio_general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=True)
463
+
464
+ aio_path = namespace_path / Path("aio")
465
+
466
+ # Write the __init__ file
467
+ self.write_file(
468
+ aio_path / Path("__init__.py"),
469
+ aio_general_serializer.serialize_init_file(clients),
470
+ )
471
+
472
+ # Write the service client
473
+ self._serialize_client_and_config_files(
474
+ namespace_path, aio_general_serializer, async_mode=True, clients=clients
475
+ )
476
+ if self.code_model.need_vendored_code(async_mode=True):
477
+ self.write_file(
478
+ aio_path / Path("_vendor.py"),
479
+ aio_general_serializer.serialize_vendor_file(clients),
480
+ )
481
+
482
+ def _serialize_and_write_metadata(self, env: Environment, namespace_path: Path) -> None:
483
+ metadata_serializer = MetadataSerializer(self.code_model, env)
484
+ self.write_file(namespace_path / Path("_metadata.json"), metadata_serializer.serialize())
485
+
486
+ @property
487
+ def _namespace_from_package_name(self) -> str:
488
+ return get_namespace_from_package_name(self.code_model.options["package_name"])
489
+
490
+ def _name_space(self) -> str:
491
+ if self.code_model.namespace.count(".") >= self._namespace_from_package_name.count("."):
492
+ return self.code_model.namespace
493
+
494
+ return self._namespace_from_package_name
495
+
496
+ # find root folder where "setup.py" is
497
+ def _package_root_folder(self, namespace_path: Path) -> Path:
498
+ return namespace_path / Path("../" * (self._name_space().count(".") + 1))
499
+
500
+ @property
501
+ def _additional_folder(self) -> Path:
502
+ namespace_config = get_namespace_config(self.code_model.namespace, self.code_model.options["multiapi"])
503
+ num_of_namespace = namespace_config.count(".") + 1
504
+ num_of_package_namespace = self._namespace_from_package_name.count(".") + 1
505
+ if num_of_namespace > num_of_package_namespace:
506
+ return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
507
+ return Path("")
508
+
509
+ def _serialize_and_write_sample(self, env: Environment, namespace_path: Path):
510
+ out_path = self._package_root_folder(namespace_path) / Path("generated_samples")
511
+ for client in self.code_model.clients:
512
+ for op_group in client.operation_groups:
513
+ for operation in op_group.operations:
514
+ if (
515
+ self.code_model.options["multiapi"]
516
+ and operation.api_versions[0] != self.code_model.options["default_api_version"]
517
+ ):
518
+ continue
519
+ samples = operation.yaml_data.get("samples")
520
+ if not samples or operation.name.startswith("_"):
521
+ continue
522
+ for value in samples.values():
523
+ file = value.get("x-ms-original-file", "sample.json")
524
+ file_name = to_snake_case(extract_sample_name(file)) + ".py"
525
+ try:
526
+ self.write_file(
527
+ out_path / self._additional_folder / _sample_output_path(file) / file_name,
528
+ SampleSerializer(
529
+ code_model=self.code_model,
530
+ env=env,
531
+ operation_group=op_group,
532
+ operation=operation,
533
+ sample=value,
534
+ file_name=file_name,
535
+ ).serialize(),
536
+ )
537
+ except Exception as e: # pylint: disable=broad-except
538
+ # sample generation shall not block code generation, so just log error
539
+ log_error = f"error happens in sample {file}: {e}"
540
+ _LOGGER.error(log_error)
541
+
542
+ def _serialize_and_write_test(self, env: Environment, namespace_path: Path):
543
+ self.code_model.for_test = True
544
+ out_path = self._package_root_folder(namespace_path) / Path("generated_tests")
545
+ general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
546
+ self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
547
+ if not self.code_model.options["azure_arm"]:
548
+ for is_async in (True, False):
549
+ async_suffix = "_async" if is_async else ""
550
+ general_serializer.is_async = is_async
551
+ self.write_file(
552
+ out_path / f"testpreparer{async_suffix}.py",
553
+ general_serializer.serialize_testpreparer(),
554
+ )
555
+
556
+ for client in self.code_model.clients:
557
+ for og in client.operation_groups:
558
+ if self.code_model.options["multiapi"] and any(
559
+ o.api_versions[0] != self.code_model.options["default_api_version"] for o in og.operations
560
+ ):
561
+ continue
562
+ test_serializer = TestSerializer(self.code_model, env, client=client, operation_group=og)
563
+ for is_async in (True, False):
564
+ try:
565
+ test_serializer.is_async = is_async
566
+ self.write_file(
567
+ out_path / f"{to_snake_case(test_serializer.test_class_name)}.py",
568
+ test_serializer.serialize_test(),
569
+ )
570
+ except Exception as e: # pylint: disable=broad-except
571
+ # test generation shall not block code generation, so just log error
572
+ log_error = f"error happens in test generation for operation group {og.class_name}: {e}"
573
+ _LOGGER.error(log_error)
574
+ self.code_model.for_test = False
@@ -0,0 +1,21 @@
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 (
8
+ FileImport,
9
+ CodeModel,
10
+ )
11
+
12
+
13
+ class BaseSerializer:
14
+ """Base serializer for SDK root level files"""
15
+
16
+ def __init__(self, code_model: CodeModel, env: Environment):
17
+ self.code_model = code_model
18
+ self.env = env
19
+
20
+ def init_file_import(self) -> FileImport:
21
+ return FileImport(self.code_model)