@typespec/http-client-python 0.14.1 → 0.14.3-dev.1
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.js +1 -3
- 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 +10 -0
- package/dist/emitter/lib.js.map +1 -1
- package/emitter/src/emitter.ts +1 -3
- package/emitter/src/lib.ts +13 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/ci/regenerate.ts +11 -4
- package/eng/scripts/setup/__pycache__/package_manager.cpython-39.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-39.pyc +0 -0
- package/generator/build/lib/pygen/__init__.py +19 -1
- package/generator/build/lib/pygen/codegen/models/code_model.py +32 -0
- 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 +79 -71
- 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 +19 -1
- package/generator/pygen/codegen/models/code_model.py +32 -0
- 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 +79 -71
- 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/generator/test/azure/requirements.txt +1 -0
- package/generator/test/azure/tox.ini +2 -2
- package/generator/test/generic_mock_api_tests/asynctests/test_authentication_async.py +7 -4
- package/generator/test/generic_mock_api_tests/test_authentication.py +7 -4
- package/generator/test/unbranded/mock_api_tests/test_unbranded.py +3 -1
- package/generator/test/unbranded/requirements.txt +1 -0
- package/generator/test/unbranded/tox.ini +2 -2
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ from collections import namedtuple
|
|
|
9
9
|
import re
|
|
10
10
|
from typing import List, Any, Union
|
|
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
|
+
generation_path / 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,25 @@ 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,
|
|
358
379
|
):
|
|
359
|
-
|
|
380
|
+
generation_path = self.code_model.get_root_dir()
|
|
360
381
|
|
|
361
382
|
def _read_version_file(original_version_file_name: str) -> str:
|
|
362
|
-
return self.read_file(
|
|
383
|
+
return self.read_file(generation_path / original_version_file_name)
|
|
363
384
|
|
|
364
385
|
def _write_version_file(original_version_file_name: str) -> None:
|
|
365
386
|
self.write_file(
|
|
366
|
-
|
|
387
|
+
generation_path / Path("_version.py"),
|
|
367
388
|
_read_version_file(original_version_file_name),
|
|
368
389
|
)
|
|
369
390
|
|
|
@@ -373,7 +394,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
373
394
|
_write_version_file(original_version_file_name="version.py")
|
|
374
395
|
elif self.code_model.options.get("package-version"):
|
|
375
396
|
self.write_file(
|
|
376
|
-
|
|
397
|
+
generation_path / Path("_version.py"),
|
|
377
398
|
general_serializer.serialize_version_file(),
|
|
378
399
|
)
|
|
379
400
|
|
|
@@ -383,47 +404,47 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
383
404
|
clients: List[Client],
|
|
384
405
|
env: Environment,
|
|
385
406
|
) -> None:
|
|
386
|
-
|
|
407
|
+
generation_path = self.code_model.get_generation_dir(namespace)
|
|
387
408
|
for async_mode, async_path in self.serialize_loop:
|
|
388
409
|
general_serializer = GeneralSerializer(
|
|
389
410
|
code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace
|
|
390
411
|
)
|
|
391
412
|
# when there is client.py, there must be __init__.py
|
|
392
413
|
self.write_file(
|
|
393
|
-
|
|
414
|
+
generation_path / Path(f"{async_path}__init__.py"),
|
|
394
415
|
general_serializer.serialize_init_file([c for c in clients if c.has_operations]),
|
|
395
416
|
)
|
|
396
417
|
|
|
397
418
|
# if there was a patch file before, we keep it
|
|
398
|
-
self._keep_patch_file(
|
|
419
|
+
self._keep_patch_file(generation_path / Path(f"{async_path}_patch.py"), env)
|
|
399
420
|
|
|
400
421
|
if self.code_model.clients_has_operations(clients):
|
|
401
422
|
|
|
402
423
|
# write client file
|
|
403
424
|
self.write_file(
|
|
404
|
-
|
|
425
|
+
generation_path / Path(f"{async_path}{self.code_model.client_filename}.py"),
|
|
405
426
|
general_serializer.serialize_service_client_file(clients),
|
|
406
427
|
)
|
|
407
428
|
|
|
408
429
|
# write config file
|
|
409
430
|
self.write_file(
|
|
410
|
-
|
|
431
|
+
generation_path / Path(f"{async_path}_configuration.py"),
|
|
411
432
|
general_serializer.serialize_config_file(clients),
|
|
412
433
|
)
|
|
413
434
|
|
|
414
435
|
# sometimes we need define additional Mixin class for client in _utils.py
|
|
415
436
|
self._serialize_and_write_utils_folder(env, namespace)
|
|
416
437
|
|
|
417
|
-
def _serialize_and_write_utils_folder(self, env: Environment, namespace: str)
|
|
418
|
-
|
|
438
|
+
def _serialize_and_write_utils_folder(self, env: Environment, namespace: str):
|
|
439
|
+
generation_dir = self.code_model.get_generation_dir(namespace)
|
|
419
440
|
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):
|
|
441
|
+
utils_folder_path = generation_dir / Path("_utils")
|
|
442
|
+
if self.code_model.need_utils_folder(async_mode=False, client_namespace=self.code_model.namespace):
|
|
422
443
|
self.write_file(
|
|
423
444
|
utils_folder_path / Path("__init__.py"),
|
|
424
445
|
self.code_model.license_header,
|
|
425
446
|
)
|
|
426
|
-
if self.code_model.need_utils_utils(async_mode=False, client_namespace=namespace):
|
|
447
|
+
if self.code_model.need_utils_utils(async_mode=False, client_namespace=self.code_model.namespace):
|
|
427
448
|
self.write_file(
|
|
428
449
|
utils_folder_path / Path("utils.py"),
|
|
429
450
|
general_serializer.need_utils_utils_file(),
|
|
@@ -443,56 +464,43 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
443
464
|
)
|
|
444
465
|
|
|
445
466
|
def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None:
|
|
446
|
-
|
|
467
|
+
root_dir = self.code_model.get_root_dir()
|
|
447
468
|
# write _utils folder
|
|
448
|
-
self._serialize_and_write_utils_folder(env, namespace)
|
|
469
|
+
self._serialize_and_write_utils_folder(env, self.code_model.namespace)
|
|
449
470
|
|
|
450
471
|
general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
|
|
451
472
|
|
|
452
473
|
# write _version.py
|
|
453
|
-
self._serialize_and_write_version_file(
|
|
474
|
+
self._serialize_and_write_version_file(general_serializer)
|
|
454
475
|
|
|
455
476
|
# write the empty py.typed file
|
|
456
|
-
|
|
477
|
+
pytyped_value = "# Marker file for PEP 561."
|
|
478
|
+
# TODO: remove this when we remove legacy multiapi generation
|
|
479
|
+
if self.code_model.options["multiapi"]:
|
|
480
|
+
self.write_file(self.code_model.get_generation_dir(namespace) / Path("py.typed"), pytyped_value)
|
|
481
|
+
else:
|
|
482
|
+
self.write_file(root_dir / Path("py.typed"), pytyped_value)
|
|
457
483
|
|
|
458
484
|
# write _validation.py
|
|
459
485
|
if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation):
|
|
460
486
|
self.write_file(
|
|
461
|
-
|
|
487
|
+
root_dir / Path("_validation.py"),
|
|
462
488
|
general_serializer.serialize_validation_file(),
|
|
463
489
|
)
|
|
464
490
|
|
|
465
491
|
# write _types.py
|
|
466
492
|
if self.code_model.named_unions:
|
|
467
493
|
self.write_file(
|
|
468
|
-
|
|
494
|
+
root_dir / Path("_types.py"),
|
|
469
495
|
TypesSerializer(code_model=self.code_model, env=env).serialize(),
|
|
470
496
|
)
|
|
471
497
|
|
|
472
498
|
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(".")
|
|
499
|
+
metadata_serializer = MetadataSerializer(self.code_model, env)
|
|
500
|
+
self.write_file(
|
|
501
|
+
self.code_model.get_generation_dir(namespace) / Path("_metadata.json"), metadata_serializer.serialize()
|
|
483
502
|
)
|
|
484
503
|
|
|
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
504
|
# pylint: disable=line-too-long
|
|
497
505
|
@property
|
|
498
506
|
def sample_additional_folder(self) -> Path:
|
|
@@ -511,8 +519,8 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
511
519
|
return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
|
|
512
520
|
return Path("")
|
|
513
521
|
|
|
514
|
-
def _serialize_and_write_sample(self, env: Environment
|
|
515
|
-
out_path =
|
|
522
|
+
def _serialize_and_write_sample(self, env: Environment):
|
|
523
|
+
out_path = Path("./generated_samples")
|
|
516
524
|
for client in self.code_model.clients:
|
|
517
525
|
for op_group in client.operation_groups:
|
|
518
526
|
for operation in op_group.operations:
|
|
@@ -544,9 +552,9 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
544
552
|
log_error = f"error happens in sample {file}: {e}"
|
|
545
553
|
_LOGGER.error(log_error)
|
|
546
554
|
|
|
547
|
-
def _serialize_and_write_test(self, env: Environment
|
|
555
|
+
def _serialize_and_write_test(self, env: Environment):
|
|
548
556
|
self.code_model.for_test = True
|
|
549
|
-
out_path =
|
|
557
|
+
out_path = Path("./generated_tests")
|
|
550
558
|
general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
|
|
551
559
|
self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
|
|
552
560
|
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
|
|
@@ -40,6 +40,7 @@ azure-mgmt-core==1.6.0
|
|
|
40
40
|
-e ./generated/authentication-http-custom
|
|
41
41
|
-e ./generated/authentication-oauth2
|
|
42
42
|
-e ./generated/authentication-union
|
|
43
|
+
-e ./generated/setuppy-authentication-union
|
|
43
44
|
-e ./generated/encode-duration
|
|
44
45
|
-e ./generated/encode-numeric
|
|
45
46
|
-e ./generated/parameters-basic
|