@autorest/python 6.37.1 → 6.38.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/autorest/__init__.py +13 -0
- package/autorest/codegen.py +1 -0
- package/generator/build/lib/pygen/__init__.py +29 -2
- package/generator/build/lib/pygen/codegen/models/code_model.py +32 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +4 -1
- package/generator/build/lib/pygen/codegen/models/model_type.py +4 -1
- package/generator/build/lib/pygen/codegen/models/operation.py +3 -4
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +1 -1
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +87 -72
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +4 -3
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +86 -13
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +1 -1
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +109 -0
- package/generator/component-detection-pip-report.json +1 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +29 -2
- package/generator/pygen/codegen/models/code_model.py +32 -0
- package/generator/pygen/codegen/models/enum_type.py +4 -1
- package/generator/pygen/codegen/models/model_type.py +4 -1
- package/generator/pygen/codegen/models/operation.py +3 -4
- package/generator/pygen/codegen/models/paging_operation.py +1 -1
- package/generator/pygen/codegen/serializers/__init__.py +87 -72
- package/generator/pygen/codegen/serializers/builder_serializer.py +4 -3
- package/generator/pygen/codegen/serializers/general_serializer.py +86 -13
- package/generator/pygen/codegen/serializers/model_serializer.py +1 -1
- package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +109 -0
- package/generator/pygen.egg-info/SOURCES.txt +1 -0
- package/package.json +2 -2
- package/scripts/__pycache__/venvtools.cpython-310.pyc +0 -0
|
@@ -7,8 +7,9 @@ import logging
|
|
|
7
7
|
import json
|
|
8
8
|
from collections import namedtuple
|
|
9
9
|
import re
|
|
10
|
-
from typing import List, Any, Union
|
|
10
|
+
from typing import List, Any, Union, Optional
|
|
11
11
|
from pathlib import Path
|
|
12
|
+
from packaging.version import parse as parse_version
|
|
12
13
|
from jinja2 import PackageLoader, Environment, FileSystemLoader, StrictUndefined
|
|
13
14
|
|
|
14
15
|
from ... import ReaderAndWriter
|
|
@@ -52,10 +53,9 @@ _PACKAGE_FILES = [
|
|
|
52
53
|
"LICENSE.jinja2",
|
|
53
54
|
"MANIFEST.in.jinja2",
|
|
54
55
|
"README.md.jinja2",
|
|
55
|
-
"setup.py.jinja2",
|
|
56
56
|
]
|
|
57
57
|
|
|
58
|
-
_REGENERATE_FILES = {"
|
|
58
|
+
_REGENERATE_FILES = {"MANIFEST.in"}
|
|
59
59
|
AsyncInfo = namedtuple("AsyncInfo", ["async_mode", "async_path"])
|
|
60
60
|
|
|
61
61
|
|
|
@@ -80,6 +80,15 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
80
80
|
) -> None:
|
|
81
81
|
super().__init__(output_folder=output_folder, **kwargs)
|
|
82
82
|
self.code_model = code_model
|
|
83
|
+
self._regenerate_setup_py()
|
|
84
|
+
|
|
85
|
+
def _regenerate_setup_py(self):
|
|
86
|
+
if self.code_model.options["keep-setup-py"] or self.code_model.options["basic-setup-py"]:
|
|
87
|
+
_PACKAGE_FILES.append("setup.py.jinja2")
|
|
88
|
+
_REGENERATE_FILES.add("setup.py")
|
|
89
|
+
else:
|
|
90
|
+
_PACKAGE_FILES.append("pyproject.toml.jinja2")
|
|
91
|
+
_REGENERATE_FILES.add("pyproject.toml")
|
|
83
92
|
|
|
84
93
|
@property
|
|
85
94
|
def has_aio_folder(self) -> bool:
|
|
@@ -101,12 +110,18 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
101
110
|
return True
|
|
102
111
|
# If the version file is already there and the version is greater than the current version, keep it.
|
|
103
112
|
try:
|
|
104
|
-
serialized_version_file = self.read_file(
|
|
113
|
+
serialized_version_file = self.read_file(
|
|
114
|
+
self.code_model.get_generation_dir(self.code_model.namespace) / "_version.py"
|
|
115
|
+
)
|
|
105
116
|
match = re.search(r'VERSION\s*=\s*"([^"]+)"', str(serialized_version_file))
|
|
106
117
|
serialized_version = match.group(1) if match else ""
|
|
107
118
|
except (FileNotFoundError, IndexError):
|
|
108
119
|
serialized_version = ""
|
|
109
|
-
|
|
120
|
+
try:
|
|
121
|
+
return parse_version(serialized_version) > parse_version(self.code_model.options.get("package-version", ""))
|
|
122
|
+
except Exception: # pylint: disable=broad-except
|
|
123
|
+
# If parsing the version fails, we assume the version file is not valid and overwrite.
|
|
124
|
+
return False
|
|
110
125
|
|
|
111
126
|
def serialize(self) -> None:
|
|
112
127
|
env = Environment(
|
|
@@ -120,34 +135,37 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
120
135
|
|
|
121
136
|
general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
|
|
122
137
|
for client_namespace, client_namespace_type in self.code_model.client_namespace_types.items():
|
|
123
|
-
|
|
138
|
+
generation_path = self.code_model.get_generation_dir(client_namespace)
|
|
124
139
|
if client_namespace == "":
|
|
125
|
-
# Write the setup file
|
|
126
140
|
if self.code_model.options["basic-setup-py"]:
|
|
127
|
-
|
|
141
|
+
# Write the setup file
|
|
142
|
+
self.write_file(generation_path / Path("setup.py"), general_serializer.serialize_setup_file())
|
|
143
|
+
elif not self.code_model.options["keep-setup-py"]:
|
|
144
|
+
# remove setup.py file
|
|
145
|
+
self.remove_file(generation_path / Path("setup.py"))
|
|
128
146
|
|
|
129
147
|
# add packaging files in root namespace (e.g. setup.py, README.md, etc.)
|
|
130
148
|
if self.code_model.options.get("package-mode"):
|
|
131
|
-
self._serialize_and_write_package_files(
|
|
149
|
+
self._serialize_and_write_package_files()
|
|
132
150
|
|
|
133
151
|
# write apiview-properties.json
|
|
134
152
|
if self.code_model.options.get("emit-cross-language-definition-file"):
|
|
135
153
|
self.write_file(
|
|
136
|
-
|
|
154
|
+
generation_path / Path("apiview-properties.json"),
|
|
137
155
|
general_serializer.serialize_cross_language_definition_file(),
|
|
138
156
|
)
|
|
139
157
|
|
|
140
158
|
# add generated samples and generated tests
|
|
141
159
|
if self.code_model.options["show-operations"] and self.code_model.has_operations:
|
|
142
160
|
if self.code_model.options["generate-sample"]:
|
|
143
|
-
self._serialize_and_write_sample(env
|
|
161
|
+
self._serialize_and_write_sample(env)
|
|
144
162
|
if self.code_model.options["generate-test"]:
|
|
145
|
-
self._serialize_and_write_test(env
|
|
163
|
+
self._serialize_and_write_test(env)
|
|
146
164
|
|
|
147
165
|
# add _metadata.json
|
|
148
166
|
if self.code_model.metadata:
|
|
149
167
|
self.write_file(
|
|
150
|
-
|
|
168
|
+
Path("./_metadata.json"),
|
|
151
169
|
json.dumps(self.code_model.metadata, indent=2),
|
|
152
170
|
)
|
|
153
171
|
elif client_namespace_type.clients:
|
|
@@ -156,7 +174,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
156
174
|
else:
|
|
157
175
|
# add pkgutil init file if no clients in this namespace
|
|
158
176
|
self.write_file(
|
|
159
|
-
|
|
177
|
+
generation_path / Path("__init__.py"),
|
|
160
178
|
general_serializer.serialize_pkgutil_init_file(),
|
|
161
179
|
)
|
|
162
180
|
|
|
@@ -178,7 +196,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
178
196
|
|
|
179
197
|
if not self.code_model.options["models-mode"]:
|
|
180
198
|
# keep models file if users ended up just writing a models file
|
|
181
|
-
model_path =
|
|
199
|
+
model_path = generation_path / Path("models.py")
|
|
182
200
|
if self.read_file(model_path):
|
|
183
201
|
self.write_file(model_path, self.read_file(model_path))
|
|
184
202
|
|
|
@@ -194,12 +212,15 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
194
212
|
# to make sure all generated files could be packed into .zip/.whl/.tgz package
|
|
195
213
|
if not client_namespace_type.clients and client_namespace_type.operation_groups and self.has_aio_folder:
|
|
196
214
|
self.write_file(
|
|
197
|
-
|
|
215
|
+
generation_path / Path("aio/__init__.py"),
|
|
198
216
|
general_serializer.serialize_pkgutil_init_file(),
|
|
199
217
|
)
|
|
200
218
|
|
|
201
|
-
def _serialize_and_write_package_files(self
|
|
202
|
-
root_of_sdk =
|
|
219
|
+
def _serialize_and_write_package_files(self) -> None:
|
|
220
|
+
root_of_sdk = Path(".")
|
|
221
|
+
if self.code_model.options["no-namespace-folders"]:
|
|
222
|
+
compensation = Path("../" * (self.code_model.namespace.count(".") + 1))
|
|
223
|
+
root_of_sdk = root_of_sdk / compensation
|
|
203
224
|
if self.code_model.options["package-mode"] in VALID_PACKAGE_MODE:
|
|
204
225
|
env = Environment(
|
|
205
226
|
loader=PackageLoader("pygen.codegen", "templates/packaging_templates"),
|
|
@@ -228,12 +249,13 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
228
249
|
file = template_name.replace(".jinja2", "")
|
|
229
250
|
output_name = root_of_sdk / file
|
|
230
251
|
if not self.read_file(output_name) or file in _REGENERATE_FILES:
|
|
231
|
-
if self.keep_version_file and file == "setup.py":
|
|
232
|
-
# don't regenerate setup.py file if the version file is more up to date
|
|
252
|
+
if self.keep_version_file and file == "setup.py" and not self.code_model.options["azure-arm"]:
|
|
253
|
+
# don't regenerate setup.py file if the version file is more up to date for data-plane
|
|
233
254
|
continue
|
|
255
|
+
file_path = self.get_output_folder() / Path(output_name)
|
|
234
256
|
self.write_file(
|
|
235
257
|
output_name,
|
|
236
|
-
serializer.serialize_package_file(template_name, **params),
|
|
258
|
+
serializer.serialize_package_file(template_name, file_path, **params),
|
|
237
259
|
)
|
|
238
260
|
|
|
239
261
|
def _keep_patch_file(self, path_file: Path, env: Environment):
|
|
@@ -249,7 +271,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
249
271
|
self, env: Environment, namespace: str, models: List[ModelType], enums: List[EnumType]
|
|
250
272
|
) -> None:
|
|
251
273
|
# Write the models folder
|
|
252
|
-
models_path = self.
|
|
274
|
+
models_path = self.code_model.get_generation_dir(namespace) / "models"
|
|
253
275
|
serializer = DpgModelSerializer if self.code_model.options["models-mode"] == "dpg" else MsrestModelSerializer
|
|
254
276
|
if self.code_model.has_non_json_models(models):
|
|
255
277
|
self.write_file(
|
|
@@ -317,7 +339,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
317
339
|
self, operation_groups: List[OperationGroup], env: Environment, namespace: str
|
|
318
340
|
) -> None:
|
|
319
341
|
operations_folder_name = self.code_model.operations_folder_name(namespace)
|
|
320
|
-
|
|
342
|
+
generation_path = self.code_model.get_generation_dir(namespace)
|
|
321
343
|
for async_mode, async_path in self.serialize_loop:
|
|
322
344
|
prefix_path = f"{async_path}{operations_folder_name}"
|
|
323
345
|
# write init file
|
|
@@ -325,7 +347,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
325
347
|
code_model=self.code_model, operation_groups=operation_groups, env=env, async_mode=async_mode
|
|
326
348
|
)
|
|
327
349
|
self.write_file(
|
|
328
|
-
|
|
350
|
+
generation_path / Path(f"{prefix_path}/__init__.py"),
|
|
329
351
|
operations_init_serializer.serialize(),
|
|
330
352
|
)
|
|
331
353
|
|
|
@@ -344,26 +366,29 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
344
366
|
client_namespace=namespace,
|
|
345
367
|
)
|
|
346
368
|
self.write_file(
|
|
347
|
-
|
|
369
|
+
generation_path / Path(f"{prefix_path}/{filename}.py"),
|
|
348
370
|
operation_group_serializer.serialize(),
|
|
349
371
|
)
|
|
350
372
|
|
|
351
373
|
# if there was a patch file before, we keep it
|
|
352
|
-
self._keep_patch_file(
|
|
374
|
+
self._keep_patch_file(generation_path / Path(f"{prefix_path}/_patch.py"), env)
|
|
353
375
|
|
|
354
376
|
def _serialize_and_write_version_file(
|
|
355
377
|
self,
|
|
356
|
-
namespace: str,
|
|
357
378
|
general_serializer: GeneralSerializer,
|
|
379
|
+
namespace: Optional[str] = None,
|
|
358
380
|
):
|
|
359
|
-
|
|
381
|
+
if namespace:
|
|
382
|
+
generation_path = self.code_model.get_generation_dir(namespace)
|
|
383
|
+
else:
|
|
384
|
+
generation_path = self.code_model.get_root_dir()
|
|
360
385
|
|
|
361
386
|
def _read_version_file(original_version_file_name: str) -> str:
|
|
362
|
-
return self.read_file(
|
|
387
|
+
return self.read_file(generation_path / original_version_file_name)
|
|
363
388
|
|
|
364
389
|
def _write_version_file(original_version_file_name: str) -> None:
|
|
365
390
|
self.write_file(
|
|
366
|
-
|
|
391
|
+
generation_path / Path("_version.py"),
|
|
367
392
|
_read_version_file(original_version_file_name),
|
|
368
393
|
)
|
|
369
394
|
|
|
@@ -373,7 +398,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
373
398
|
_write_version_file(original_version_file_name="version.py")
|
|
374
399
|
elif self.code_model.options.get("package-version"):
|
|
375
400
|
self.write_file(
|
|
376
|
-
|
|
401
|
+
generation_path / Path("_version.py"),
|
|
377
402
|
general_serializer.serialize_version_file(),
|
|
378
403
|
)
|
|
379
404
|
|
|
@@ -383,47 +408,47 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
383
408
|
clients: List[Client],
|
|
384
409
|
env: Environment,
|
|
385
410
|
) -> None:
|
|
386
|
-
|
|
411
|
+
generation_path = self.code_model.get_generation_dir(namespace)
|
|
387
412
|
for async_mode, async_path in self.serialize_loop:
|
|
388
413
|
general_serializer = GeneralSerializer(
|
|
389
414
|
code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace
|
|
390
415
|
)
|
|
391
416
|
# when there is client.py, there must be __init__.py
|
|
392
417
|
self.write_file(
|
|
393
|
-
|
|
418
|
+
generation_path / Path(f"{async_path}__init__.py"),
|
|
394
419
|
general_serializer.serialize_init_file([c for c in clients if c.has_operations]),
|
|
395
420
|
)
|
|
396
421
|
|
|
397
422
|
# if there was a patch file before, we keep it
|
|
398
|
-
self._keep_patch_file(
|
|
423
|
+
self._keep_patch_file(generation_path / Path(f"{async_path}_patch.py"), env)
|
|
399
424
|
|
|
400
425
|
if self.code_model.clients_has_operations(clients):
|
|
401
426
|
|
|
402
427
|
# write client file
|
|
403
428
|
self.write_file(
|
|
404
|
-
|
|
429
|
+
generation_path / Path(f"{async_path}{self.code_model.client_filename}.py"),
|
|
405
430
|
general_serializer.serialize_service_client_file(clients),
|
|
406
431
|
)
|
|
407
432
|
|
|
408
433
|
# write config file
|
|
409
434
|
self.write_file(
|
|
410
|
-
|
|
435
|
+
generation_path / Path(f"{async_path}_configuration.py"),
|
|
411
436
|
general_serializer.serialize_config_file(clients),
|
|
412
437
|
)
|
|
413
438
|
|
|
414
439
|
# sometimes we need define additional Mixin class for client in _utils.py
|
|
415
440
|
self._serialize_and_write_utils_folder(env, namespace)
|
|
416
441
|
|
|
417
|
-
def _serialize_and_write_utils_folder(self, env: Environment, namespace: str)
|
|
418
|
-
|
|
442
|
+
def _serialize_and_write_utils_folder(self, env: Environment, namespace: str):
|
|
443
|
+
generation_dir = self.code_model.get_generation_dir(namespace)
|
|
419
444
|
general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
|
|
420
|
-
utils_folder_path =
|
|
421
|
-
if self.code_model.need_utils_folder(async_mode=False, client_namespace=namespace):
|
|
445
|
+
utils_folder_path = generation_dir / Path("_utils")
|
|
446
|
+
if self.code_model.need_utils_folder(async_mode=False, client_namespace=self.code_model.namespace):
|
|
422
447
|
self.write_file(
|
|
423
448
|
utils_folder_path / Path("__init__.py"),
|
|
424
449
|
self.code_model.license_header,
|
|
425
450
|
)
|
|
426
|
-
if self.code_model.need_utils_utils(async_mode=False, client_namespace=namespace):
|
|
451
|
+
if self.code_model.need_utils_utils(async_mode=False, client_namespace=self.code_model.namespace):
|
|
427
452
|
self.write_file(
|
|
428
453
|
utils_folder_path / Path("utils.py"),
|
|
429
454
|
general_serializer.need_utils_utils_file(),
|
|
@@ -443,56 +468,46 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
443
468
|
)
|
|
444
469
|
|
|
445
470
|
def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None:
|
|
446
|
-
|
|
471
|
+
root_dir = self.code_model.get_root_dir()
|
|
447
472
|
# write _utils folder
|
|
448
|
-
self._serialize_and_write_utils_folder(env, namespace)
|
|
473
|
+
self._serialize_and_write_utils_folder(env, self.code_model.namespace)
|
|
449
474
|
|
|
450
475
|
general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
|
|
451
476
|
|
|
452
477
|
# write _version.py
|
|
453
|
-
self._serialize_and_write_version_file(
|
|
478
|
+
self._serialize_and_write_version_file(general_serializer)
|
|
479
|
+
# if there's a subdir, we need to write another version file in the subdir
|
|
480
|
+
if self.code_model.options.get("generation-subdir"):
|
|
481
|
+
self._serialize_and_write_version_file(general_serializer, namespace)
|
|
454
482
|
|
|
455
483
|
# write the empty py.typed file
|
|
456
|
-
|
|
484
|
+
pytyped_value = "# Marker file for PEP 561."
|
|
485
|
+
# TODO: remove this when we remove legacy multiapi generation
|
|
486
|
+
if self.code_model.options["multiapi"]:
|
|
487
|
+
self.write_file(self.code_model.get_generation_dir(namespace) / Path("py.typed"), pytyped_value)
|
|
488
|
+
else:
|
|
489
|
+
self.write_file(root_dir / Path("py.typed"), pytyped_value)
|
|
457
490
|
|
|
458
491
|
# write _validation.py
|
|
459
492
|
if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation):
|
|
460
493
|
self.write_file(
|
|
461
|
-
|
|
494
|
+
root_dir / Path("_validation.py"),
|
|
462
495
|
general_serializer.serialize_validation_file(),
|
|
463
496
|
)
|
|
464
497
|
|
|
465
498
|
# write _types.py
|
|
466
499
|
if self.code_model.named_unions:
|
|
467
500
|
self.write_file(
|
|
468
|
-
|
|
501
|
+
root_dir / Path("_types.py"),
|
|
469
502
|
TypesSerializer(code_model=self.code_model, env=env).serialize(),
|
|
470
503
|
)
|
|
471
504
|
|
|
472
505
|
def _serialize_and_write_metadata(self, env: Environment, namespace: str) -> None:
|
|
473
|
-
metadata_serializer = MetadataSerializer(self.code_model, env
|
|
474
|
-
self.write_file(
|
|
475
|
-
|
|
476
|
-
@property
|
|
477
|
-
def exec_path_compensation(self) -> Path:
|
|
478
|
-
"""Assume the process is running in the root folder of the package. If not, we need the path compensation."""
|
|
479
|
-
return (
|
|
480
|
-
Path("../" * (self.code_model.namespace.count(".") + 1))
|
|
481
|
-
if self.code_model.options["no-namespace-folders"]
|
|
482
|
-
else Path(".")
|
|
506
|
+
metadata_serializer = MetadataSerializer(self.code_model, env)
|
|
507
|
+
self.write_file(
|
|
508
|
+
self.code_model.get_generation_dir(namespace) / Path("_metadata.json"), metadata_serializer.serialize()
|
|
483
509
|
)
|
|
484
510
|
|
|
485
|
-
def exec_path_for_test_sample(self, namespace: str) -> Path:
|
|
486
|
-
return self.exec_path_compensation / Path(*namespace.split("."))
|
|
487
|
-
|
|
488
|
-
# pylint: disable=line-too-long
|
|
489
|
-
def exec_path(self, namespace: str) -> Path:
|
|
490
|
-
if self.code_model.options["no-namespace-folders"] and not self.code_model.options["multiapi"]:
|
|
491
|
-
# when output folder contains parts different from the namespace, we fall back to current folder directly.
|
|
492
|
-
# (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md)
|
|
493
|
-
return Path(".")
|
|
494
|
-
return self.exec_path_compensation / Path(*namespace.split("."))
|
|
495
|
-
|
|
496
511
|
# pylint: disable=line-too-long
|
|
497
512
|
@property
|
|
498
513
|
def sample_additional_folder(self) -> Path:
|
|
@@ -511,8 +526,8 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
511
526
|
return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
|
|
512
527
|
return Path("")
|
|
513
528
|
|
|
514
|
-
def _serialize_and_write_sample(self, env: Environment
|
|
515
|
-
out_path =
|
|
529
|
+
def _serialize_and_write_sample(self, env: Environment):
|
|
530
|
+
out_path = Path("./generated_samples")
|
|
516
531
|
for client in self.code_model.clients:
|
|
517
532
|
for op_group in client.operation_groups:
|
|
518
533
|
for operation in op_group.operations:
|
|
@@ -544,9 +559,9 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
544
559
|
log_error = f"error happens in sample {file}: {e}"
|
|
545
560
|
_LOGGER.error(log_error)
|
|
546
561
|
|
|
547
|
-
def _serialize_and_write_test(self, env: Environment
|
|
562
|
+
def _serialize_and_write_test(self, env: Environment):
|
|
548
563
|
self.code_model.for_test = True
|
|
549
|
-
out_path =
|
|
564
|
+
out_path = Path("./generated_tests")
|
|
550
565
|
general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
|
|
551
566
|
self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
|
|
552
567
|
if not self.code_model.options["azure-arm"]:
|
|
@@ -1079,18 +1079,19 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
1079
1079
|
)
|
|
1080
1080
|
condition = "elif"
|
|
1081
1081
|
# default error handling
|
|
1082
|
-
|
|
1082
|
+
default_error_deserialization = builder.default_error_deserialization(self.serialize_namespace)
|
|
1083
|
+
if default_error_deserialization and self.code_model.options["models-mode"]:
|
|
1083
1084
|
error_model = ", model=error"
|
|
1084
1085
|
indent = " " if builder.non_default_errors else " "
|
|
1085
1086
|
if builder.non_default_errors:
|
|
1086
1087
|
retval.append(" else:")
|
|
1087
1088
|
if self.code_model.options["models-mode"] == "dpg":
|
|
1088
1089
|
retval.append(
|
|
1089
|
-
f"{indent}error = _failsafe_deserialize({
|
|
1090
|
+
f"{indent}error = _failsafe_deserialize({default_error_deserialization}, response.json())"
|
|
1090
1091
|
)
|
|
1091
1092
|
else:
|
|
1092
1093
|
retval.append(
|
|
1093
|
-
f"{indent}error = self._deserialize.failsafe_deserialize({
|
|
1094
|
+
f"{indent}error = self._deserialize.failsafe_deserialize({default_error_deserialization}, "
|
|
1094
1095
|
"pipeline_response)"
|
|
1095
1096
|
)
|
|
1096
1097
|
retval.append(
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
import json
|
|
7
|
-
from typing import Any, List
|
|
7
|
+
from typing import Any, List, TYPE_CHECKING
|
|
8
|
+
import re
|
|
9
|
+
import tomli as tomllib
|
|
10
|
+
from packaging.version import parse as parse_version
|
|
8
11
|
from .import_serializer import FileImportSerializer, TypingSection
|
|
9
12
|
from ..models.imports import MsrestImportType, FileImport
|
|
10
13
|
from ..models import (
|
|
@@ -16,6 +19,9 @@ from ..models.utils import NamespaceType
|
|
|
16
19
|
from .client_serializer import ClientSerializer, ConfigSerializer
|
|
17
20
|
from .base_serializer import BaseSerializer
|
|
18
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
19
25
|
VERSION_MAP = {
|
|
20
26
|
"msrest": "0.7.1",
|
|
21
27
|
"isodate": "0.6.1",
|
|
@@ -42,8 +48,73 @@ class GeneralSerializer(BaseSerializer):
|
|
|
42
48
|
params.update({"options": self.code_model.options})
|
|
43
49
|
return template.render(code_model=self.code_model, **params)
|
|
44
50
|
|
|
45
|
-
def
|
|
51
|
+
def _extract_min_dependency(self, s):
|
|
52
|
+
# Extract the minimum version from a dependency string.
|
|
53
|
+
#
|
|
54
|
+
# Handles formats like:
|
|
55
|
+
# - >=1.2.3
|
|
56
|
+
# - >=0.1.0b1 (beta versions)
|
|
57
|
+
# - >=1.2.3rc2 (release candidates)
|
|
58
|
+
#
|
|
59
|
+
# Returns the parsed version if found, otherwise version "0".
|
|
60
|
+
m = re.search(r"[>=]=?([\d.]+(?:[a-z]+\d+)?)", s)
|
|
61
|
+
return parse_version(m.group(1)) if m else parse_version("0")
|
|
62
|
+
|
|
63
|
+
def _keep_pyproject_fields(self, file_path: "Path") -> dict:
|
|
64
|
+
# Load the pyproject.toml file if it exists and extract fields to keep.
|
|
65
|
+
result: dict = {"KEEP_FIELDS": {}}
|
|
66
|
+
try:
|
|
67
|
+
with open(file_path, "rb") as f:
|
|
68
|
+
loaded_pyproject_toml = tomllib.load(f)
|
|
69
|
+
except Exception: # pylint: disable=broad-except
|
|
70
|
+
# If parsing the pyproject.toml fails, we assume the it does not exist or is incorrectly formatted.
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
# Keep azure-sdk-build configuration
|
|
74
|
+
if "tool" in loaded_pyproject_toml and "azure-sdk-build" in loaded_pyproject_toml["tool"]:
|
|
75
|
+
result["KEEP_FIELDS"]["tool.azure-sdk-build"] = loaded_pyproject_toml["tool"]["azure-sdk-build"]
|
|
76
|
+
|
|
77
|
+
# Process dependencies
|
|
78
|
+
if "project" in loaded_pyproject_toml:
|
|
79
|
+
# Handle main dependencies
|
|
80
|
+
if "dependencies" in loaded_pyproject_toml["project"]:
|
|
81
|
+
kept_deps = []
|
|
82
|
+
for dep in loaded_pyproject_toml["project"]["dependencies"]:
|
|
83
|
+
dep_name = re.split(r"[<>=\[]", dep)[0].strip()
|
|
84
|
+
|
|
85
|
+
# Check if dependency is one we track in VERSION_MAP
|
|
86
|
+
if dep_name in VERSION_MAP:
|
|
87
|
+
# For tracked dependencies, check if the version is higher than our default
|
|
88
|
+
default_version = parse_version(VERSION_MAP[dep_name])
|
|
89
|
+
dep_version = self._extract_min_dependency(dep)
|
|
90
|
+
# If the version is higher than the default, update VERSION_MAP
|
|
91
|
+
# with higher min dependency version
|
|
92
|
+
if dep_version > default_version:
|
|
93
|
+
VERSION_MAP[dep_name] = str(dep_version)
|
|
94
|
+
else:
|
|
95
|
+
# Keep non-default dependencies
|
|
96
|
+
kept_deps.append(dep)
|
|
97
|
+
|
|
98
|
+
if kept_deps:
|
|
99
|
+
result["KEEP_FIELDS"]["project.dependencies"] = kept_deps
|
|
100
|
+
|
|
101
|
+
# Keep optional dependencies
|
|
102
|
+
if "optional-dependencies" in loaded_pyproject_toml["project"]:
|
|
103
|
+
result["KEEP_FIELDS"]["project.optional-dependencies"] = loaded_pyproject_toml["project"][
|
|
104
|
+
"optional-dependencies"
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
def serialize_package_file(self, template_name: str, file_path: "Path", **kwargs: Any) -> str:
|
|
46
110
|
template = self.env.get_template(template_name)
|
|
111
|
+
|
|
112
|
+
# Add fields to keep from an existing pyproject.toml
|
|
113
|
+
if template_name == "pyproject.toml.jinja2":
|
|
114
|
+
params = self._keep_pyproject_fields(file_path)
|
|
115
|
+
else:
|
|
116
|
+
params = {}
|
|
117
|
+
|
|
47
118
|
package_parts = (
|
|
48
119
|
self.code_model.namespace.split(".")[:-1]
|
|
49
120
|
if self.code_model.is_tsp
|
|
@@ -57,17 +128,19 @@ class GeneralSerializer(BaseSerializer):
|
|
|
57
128
|
dev_status = "4 - Beta"
|
|
58
129
|
else:
|
|
59
130
|
dev_status = "5 - Production/Stable"
|
|
60
|
-
params
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
131
|
+
params.update(
|
|
132
|
+
{
|
|
133
|
+
"code_model": self.code_model,
|
|
134
|
+
"dev_status": dev_status,
|
|
135
|
+
"token_credential": token_credential,
|
|
136
|
+
"pkgutil_names": [".".join(package_parts[: i + 1]) for i in range(len(package_parts))],
|
|
137
|
+
"init_names": ["/".join(package_parts[: i + 1]) + "/__init__.py" for i in range(len(package_parts))],
|
|
138
|
+
"client_name": self.code_model.clients[0].name if self.code_model.clients else "",
|
|
139
|
+
"VERSION_MAP": VERSION_MAP,
|
|
140
|
+
"MIN_PYTHON_VERSION": MIN_PYTHON_VERSION,
|
|
141
|
+
"MAX_PYTHON_VERSION": MAX_PYTHON_VERSION,
|
|
142
|
+
}
|
|
143
|
+
)
|
|
71
144
|
params.update({"options": self.code_model.options})
|
|
72
145
|
params.update(kwargs)
|
|
73
146
|
return template.render(file_import=FileImport(self.code_model), **params)
|
|
@@ -266,7 +266,7 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
266
266
|
)
|
|
267
267
|
if model.is_polymorphic:
|
|
268
268
|
file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB)
|
|
269
|
-
if
|
|
269
|
+
if self.need_init(model):
|
|
270
270
|
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
271
271
|
file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
|
|
272
272
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{% set min_version = MIN_PYTHON_VERSION.split('.')[1] | int %}
|
|
2
|
+
{% set max_version = MAX_PYTHON_VERSION.split('.')[1] | int %}
|
|
3
|
+
{% if code_model.license_header %}
|
|
4
|
+
{{ code_model.license_header }}
|
|
5
|
+
{% endif %}
|
|
6
|
+
|
|
7
|
+
[build-system]
|
|
8
|
+
requires = ["setuptools>=61.0.0", "wheel"] # Requires 61.0.0 for dynamic version
|
|
9
|
+
build-backend = "setuptools.build_meta"
|
|
10
|
+
|
|
11
|
+
[project]
|
|
12
|
+
name = "{{ options.get('package-name')|lower }}"
|
|
13
|
+
{% if options.get('package-mode') %}
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "{{ code_model.company_name }}"{% if code_model.is_azure_flavor %}, email = "azpysdkhelp@microsoft.com"{% endif %} },
|
|
16
|
+
]
|
|
17
|
+
description = "{{ code_model.company_name }} {% if code_model.is_azure_flavor and not options.get('package-pprint-name').startswith('Azure ') %}Azure {% endif %}{{ options.get('package-pprint-name') }} Client Library for Python"
|
|
18
|
+
license = {text = "MIT License"}
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: {{ dev_status }}",
|
|
21
|
+
"Programming Language :: Python",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
{% for version in range(min_version, max_version + 1) %}
|
|
25
|
+
"Programming Language :: Python :: 3.{{ version }}",
|
|
26
|
+
{% endfor %}
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
]
|
|
29
|
+
requires-python = ">={{ MIN_PYTHON_VERSION }}"
|
|
30
|
+
{% else %}
|
|
31
|
+
description = "{{ options.get('package-name') }}"
|
|
32
|
+
{% endif %}
|
|
33
|
+
{% if code_model.is_azure_flavor %}
|
|
34
|
+
keywords = ["azure", "azure sdk"]
|
|
35
|
+
{% endif %}
|
|
36
|
+
|
|
37
|
+
dependencies = [
|
|
38
|
+
{% if code_model.is_legacy %}
|
|
39
|
+
"msrest>={{ VERSION_MAP['msrest'] }}",
|
|
40
|
+
{% else %}
|
|
41
|
+
"isodate>={{ VERSION_MAP['isodate'] }}",
|
|
42
|
+
{% endif %}
|
|
43
|
+
{% if options.get('azure_arm') %}
|
|
44
|
+
"azure-mgmt-core>={{ VERSION_MAP['azure-mgmt-core'] }}",
|
|
45
|
+
{% elif code_model.is_azure_flavor %}
|
|
46
|
+
"azure-core>={{ VERSION_MAP['azure-core'] }}",
|
|
47
|
+
{% else %}
|
|
48
|
+
"corehttp[requests]>={{ VERSION_MAP['corehttp'] }}",
|
|
49
|
+
{% endif %}
|
|
50
|
+
"typing-extensions>={{ VERSION_MAP['typing-extensions'] }}",
|
|
51
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.dependencies') %}
|
|
52
|
+
{% for dep in KEEP_FIELDS.get('project.dependencies') %}
|
|
53
|
+
"{{ dep }}",
|
|
54
|
+
{% endfor %}
|
|
55
|
+
{% endif %}
|
|
56
|
+
]
|
|
57
|
+
dynamic = [
|
|
58
|
+
{% if options.get('package-mode') %}"version", {% endif %}"readme"
|
|
59
|
+
]
|
|
60
|
+
{% if not options.get('package-mode') %}
|
|
61
|
+
version = "{{ options.get("package-version", "unknown") }}"
|
|
62
|
+
{% endif %}
|
|
63
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.optional-dependencies') %}
|
|
64
|
+
|
|
65
|
+
[project.optional-dependencies]
|
|
66
|
+
{% for key, val in KEEP_FIELDS.get('project.optional-dependencies').items() %}
|
|
67
|
+
{{ key }} = [
|
|
68
|
+
{% for dep in val %}
|
|
69
|
+
"{{ dep }}",
|
|
70
|
+
{% endfor %}
|
|
71
|
+
]
|
|
72
|
+
{% endfor %}
|
|
73
|
+
{% endif %}
|
|
74
|
+
{% if code_model.is_azure_flavor %}
|
|
75
|
+
|
|
76
|
+
[project.urls]
|
|
77
|
+
repository = "https://github.com/Azure/azure-sdk-for-python/tree/main/sdk"
|
|
78
|
+
{% endif %}
|
|
79
|
+
|
|
80
|
+
[tool.setuptools.dynamic]
|
|
81
|
+
{% if options.get('package-mode') %}
|
|
82
|
+
{% if code_model.is_tsp %}
|
|
83
|
+
version = {attr = "{{ code_model.namespace|lower }}._version.VERSION"}
|
|
84
|
+
{% else %}
|
|
85
|
+
version = {attr = "{{ options.get('package-name')|lower|replace('-', '.') }}._version.VERSION"}
|
|
86
|
+
{% endif %}
|
|
87
|
+
{% endif %}
|
|
88
|
+
readme = {file = ["README.md"], content-type = "text/markdown"}
|
|
89
|
+
{% if options.get('package-mode') %}
|
|
90
|
+
|
|
91
|
+
[tool.setuptools.packages.find]
|
|
92
|
+
exclude = [
|
|
93
|
+
"tests*",
|
|
94
|
+
"samples*",
|
|
95
|
+
{% for pkgutil_name in pkgutil_names %}
|
|
96
|
+
"{{ pkgutil_name }}",
|
|
97
|
+
{% endfor %}
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[tool.setuptools.package-data]
|
|
101
|
+
pytyped = ["py.typed"]
|
|
102
|
+
{% endif %}
|
|
103
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('tool.azure-sdk-build') %}
|
|
104
|
+
|
|
105
|
+
[tool.azure-sdk-build]
|
|
106
|
+
{% for key, val in KEEP_FIELDS.get('tool.azure-sdk-build').items() %}
|
|
107
|
+
{{ key }} = {{ val|tojson }}
|
|
108
|
+
{% endfor %}
|
|
109
|
+
{% endif %}
|
|
@@ -100,6 +100,7 @@ pygen/codegen/templates/packaging_templates/LICENSE.jinja2
|
|
|
100
100
|
pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2
|
|
101
101
|
pygen/codegen/templates/packaging_templates/README.md.jinja2
|
|
102
102
|
pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2
|
|
103
|
+
pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2
|
|
103
104
|
pygen/codegen/templates/packaging_templates/setup.py.jinja2
|
|
104
105
|
pygen/preprocess/__init__.py
|
|
105
106
|
pygen/preprocess/helpers.py
|