@autorest/python 6.37.2 → 6.38.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/autorest/__init__.py +13 -0
- package/autorest/codegen.py +1 -0
- package/generator/build/lib/pygen/__init__.py +32 -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 +85 -74
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +7 -6
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +86 -13
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +5 -4
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +109 -0
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +32 -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 +85 -74
- package/generator/pygen/codegen/serializers/builder_serializer.py +7 -6
- package/generator/pygen/codegen/serializers/general_serializer.py +86 -13
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +5 -4
- 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
package/autorest/__init__.py
CHANGED
|
@@ -22,12 +22,25 @@ class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
|
22
22
|
super().__init__(output_folder=output_folder)
|
|
23
23
|
self._autorestapi = autorestapi
|
|
24
24
|
|
|
25
|
+
def get_output_folder(self) -> Path:
|
|
26
|
+
# Get the output folder from AutoRest configuration to resolve against the correct base and
|
|
27
|
+
# convert URI to file system path by removing file:// prefix if present
|
|
28
|
+
return Path(self._autorestapi.get_value("outputFolderUri").lstrip("file:"))
|
|
29
|
+
|
|
25
30
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
26
31
|
return self._autorestapi.read_file(path)
|
|
27
32
|
|
|
28
33
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
29
34
|
return self._autorestapi.write_file(filename, file_content)
|
|
30
35
|
|
|
36
|
+
def remove_file(self, filename: Union[str, Path]) -> None:
|
|
37
|
+
try:
|
|
38
|
+
file_path = self.get_output_folder() / Path(filename)
|
|
39
|
+
if file_path.is_file():
|
|
40
|
+
file_path.unlink()
|
|
41
|
+
except (FileNotFoundError, OSError):
|
|
42
|
+
pass
|
|
43
|
+
|
|
31
44
|
def list_file(self) -> List[str]:
|
|
32
45
|
return self._autorestapi.list_inputs()
|
|
33
46
|
|
package/autorest/codegen.py
CHANGED
|
@@ -64,6 +64,7 @@ class CodeGeneratorAutorest(CodeGenerator, PluginAutorest):
|
|
|
64
64
|
"no-async": self._autorestapi.get_boolean_value("no-async"),
|
|
65
65
|
"no-namespace-folders": self._autorestapi.get_boolean_value("no-namespace-folders"),
|
|
66
66
|
"basic-setup-py": self._autorestapi.get_boolean_value("basic-setup-py"),
|
|
67
|
+
"keep-setup-py": self._autorestapi.get_boolean_value("keep-setup-py"),
|
|
67
68
|
"package-name": self._autorestapi.get_value("package-name"),
|
|
68
69
|
"package-version": self._autorestapi.get_value("package-version"),
|
|
69
70
|
"client-side-validation": self._autorestapi.get_boolean_value("client-side-validation"),
|
|
@@ -25,10 +25,10 @@ class OptionsDict(MutableMapping):
|
|
|
25
25
|
"azure-arm": False,
|
|
26
26
|
"basic-setup-py": False,
|
|
27
27
|
"client-side-validation": False,
|
|
28
|
-
"emit-cross-language-definition-file": False,
|
|
29
28
|
"flavor": "azure", # need to default to azure in shared code so we don't break swagger generation
|
|
30
29
|
"from-typespec": False,
|
|
31
30
|
"generate-sample": False,
|
|
31
|
+
"keep-setup-py": False,
|
|
32
32
|
"generate-test": False,
|
|
33
33
|
"head-as-boolean": True,
|
|
34
34
|
"keep-version-file": False,
|
|
@@ -39,13 +39,14 @@ class OptionsDict(MutableMapping):
|
|
|
39
39
|
"polymorphic-examples": 5,
|
|
40
40
|
"validate-versioning": True,
|
|
41
41
|
"version-tolerant": True,
|
|
42
|
+
"generation-subdir": None, # subdirectory to generate the code in
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
def __init__(self, options: Optional[Dict[str, Any]] = None) -> None:
|
|
45
46
|
self._data = options.copy() if options else {}
|
|
46
47
|
self._validate_combinations()
|
|
47
48
|
|
|
48
|
-
def __getitem__(self, key: str) -> Any:
|
|
49
|
+
def __getitem__(self, key: str) -> Any: # pylint: disable=too-many-return-statements
|
|
49
50
|
if key == "head-as-boolean" and self.get("azure-arm"):
|
|
50
51
|
# override to always true if azure-arm is set
|
|
51
52
|
return True
|
|
@@ -60,6 +61,18 @@ class OptionsDict(MutableMapping):
|
|
|
60
61
|
if key == "package-mode" and self._data.get("packaging-files-dir"):
|
|
61
62
|
# if packaging-files-dir is set, use it as package-mode
|
|
62
63
|
return self._data["packaging-files-dir"]
|
|
64
|
+
if key == "generation-subdir":
|
|
65
|
+
data = self._data.get("generation-subdir")
|
|
66
|
+
if data:
|
|
67
|
+
# Remove leading dot or ./ from generation-subdir
|
|
68
|
+
if data.startswith("./"):
|
|
69
|
+
data = data[2:]
|
|
70
|
+
elif data.startswith("."):
|
|
71
|
+
data = data[1:]
|
|
72
|
+
# Remove trailing slashes
|
|
73
|
+
if data.endswith("/") or data.endswith("\\"):
|
|
74
|
+
data = data[:-1]
|
|
75
|
+
return data
|
|
63
76
|
return self._get_default(key)
|
|
64
77
|
|
|
65
78
|
def __setitem__(self, key: str, value: Any) -> None:
|
|
@@ -106,6 +119,8 @@ class OptionsDict(MutableMapping):
|
|
|
106
119
|
models_mode_default = "dpg"
|
|
107
120
|
# switch to falsy value for easier code writing
|
|
108
121
|
return models_mode_default
|
|
122
|
+
if key == "emit-cross-language-definition-file":
|
|
123
|
+
return self.get("flavor") == "azure"
|
|
109
124
|
return self.DEFAULTS[key]
|
|
110
125
|
|
|
111
126
|
def _validate_combinations(self) -> None:
|
|
@@ -134,6 +149,10 @@ class OptionsDict(MutableMapping):
|
|
|
134
149
|
"We are working on creating a new multiapi SDK for version tolerant and it is not available yet."
|
|
135
150
|
)
|
|
136
151
|
|
|
152
|
+
# If multiapi, do not generate default pyproject.toml
|
|
153
|
+
if self.get("multiapi"):
|
|
154
|
+
self["keep-setup-py"] = True
|
|
155
|
+
|
|
137
156
|
if self.get("client-side-validation") and self.get("version-tolerant"):
|
|
138
157
|
raise ValueError("Can not generate version tolerant with --client-side-validation. ")
|
|
139
158
|
|
|
@@ -210,6 +229,9 @@ class ReaderAndWriter:
|
|
|
210
229
|
_LOGGER.warning("Loading python.json file. This behavior will be depreacted")
|
|
211
230
|
self.options.update(python_json)
|
|
212
231
|
|
|
232
|
+
def get_output_folder(self) -> Path:
|
|
233
|
+
return self.output_folder
|
|
234
|
+
|
|
213
235
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
214
236
|
"""Directly reading from disk"""
|
|
215
237
|
# make path relative to output folder
|
|
@@ -227,6 +249,14 @@ class ReaderAndWriter:
|
|
|
227
249
|
with open(self.output_folder / Path(filename), "w", encoding="utf-8") as fd:
|
|
228
250
|
fd.write(file_content)
|
|
229
251
|
|
|
252
|
+
def remove_file(self, filename: Union[str, Path]) -> None:
|
|
253
|
+
try:
|
|
254
|
+
file_path = self.output_folder / Path(filename)
|
|
255
|
+
if file_path.is_file():
|
|
256
|
+
file_path.unlink()
|
|
257
|
+
except FileNotFoundError:
|
|
258
|
+
pass
|
|
259
|
+
|
|
230
260
|
def list_file(self) -> List[str]:
|
|
231
261
|
return [str(f.relative_to(self.output_folder)) for f in self.output_folder.glob("**/*") if f.is_file()]
|
|
232
262
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
from typing import List, Dict, Any, Set, Union, Literal, Optional, cast
|
|
7
8
|
|
|
8
9
|
from .base import BaseType
|
|
@@ -452,3 +453,34 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
452
453
|
return self.yaml_data.get("licenseInfo", {}).get("company", "")
|
|
453
454
|
# typespec azure case without custom license and swagger case
|
|
454
455
|
return "Microsoft Corporation"
|
|
456
|
+
|
|
457
|
+
def get_root_dir(self) -> Path:
|
|
458
|
+
if self.options["no-namespace-folders"]:
|
|
459
|
+
# when output folder contains parts different from the namespace, we fall back to current folder directly.
|
|
460
|
+
return Path(".")
|
|
461
|
+
return Path(*self.namespace.split("."))
|
|
462
|
+
|
|
463
|
+
def get_generation_dir(self, namespace: str) -> Path:
|
|
464
|
+
"""The directory to generate the code in.
|
|
465
|
+
|
|
466
|
+
If 'generation-subdir' is specified, it will be used as a subdirectory.
|
|
467
|
+
"""
|
|
468
|
+
root_dir = self.get_root_dir()
|
|
469
|
+
retval = self._get_relative_generation_dir(root_dir, namespace)
|
|
470
|
+
return retval
|
|
471
|
+
|
|
472
|
+
def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path:
|
|
473
|
+
if self.options["no-namespace-folders"]:
|
|
474
|
+
return Path(".")
|
|
475
|
+
if self.options.get("generation-subdir"):
|
|
476
|
+
# For the main namespace, return root_dir + generation-subdir
|
|
477
|
+
if namespace in ("", self.namespace):
|
|
478
|
+
return root_dir / self.options["generation-subdir"]
|
|
479
|
+
|
|
480
|
+
# For subnamespaces, extract the subnamespace part and append it to generation-subdir
|
|
481
|
+
if namespace.startswith(self.namespace + "."):
|
|
482
|
+
subnamespace_parts = namespace[len(self.namespace) + 1 :].split(".")
|
|
483
|
+
return root_dir / self.options["generation-subdir"] / Path(*subnamespace_parts)
|
|
484
|
+
|
|
485
|
+
# No generation-subdir specified, use the namespace path directly
|
|
486
|
+
return Path(*namespace.split("."))
|
|
@@ -52,7 +52,10 @@ class EnumValue(BaseType):
|
|
|
52
52
|
"""The python type used for RST syntax input and type annotation."""
|
|
53
53
|
|
|
54
54
|
type_annotation = self.value_type.type_annotation(**kwargs)
|
|
55
|
-
|
|
55
|
+
client_namespace = self.enum_type.client_namespace
|
|
56
|
+
if self.code_model.options.get("generation-subdir"):
|
|
57
|
+
client_namespace += f".{self.code_model.options['generation-subdir']}"
|
|
58
|
+
enum_type_annotation = f"{client_namespace}.models.{self.name}"
|
|
56
59
|
return f"{type_annotation} or ~{enum_type_annotation}"
|
|
57
60
|
|
|
58
61
|
def get_json_template_representation(
|
|
@@ -292,7 +292,10 @@ class GeneratedModelType(ModelType):
|
|
|
292
292
|
|
|
293
293
|
def docstring_type(self, **kwargs: Any) -> str:
|
|
294
294
|
type_annotation = self.type_annotation(need_model_alias=False, skip_quote=True, **kwargs)
|
|
295
|
-
|
|
295
|
+
client_namespace = self.client_namespace
|
|
296
|
+
if self.code_model.options.get("generation-subdir"):
|
|
297
|
+
client_namespace += f".{self.code_model.options['generation-subdir']}"
|
|
298
|
+
return f"~{client_namespace}.models.{type_annotation}"
|
|
296
299
|
|
|
297
300
|
def docstring_text(self, **kwargs: Any) -> str:
|
|
298
301
|
return self.name
|
|
@@ -194,14 +194,13 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
194
194
|
def any_response_has_headers(self) -> bool:
|
|
195
195
|
return any(response.headers for response in self.responses)
|
|
196
196
|
|
|
197
|
-
|
|
198
|
-
def default_error_deserialization(self) -> Optional[str]:
|
|
197
|
+
def default_error_deserialization(self, serialize_namespace: str) -> Optional[str]:
|
|
199
198
|
default_exceptions = [e for e in self.exceptions if "default" in e.status_codes and e.type]
|
|
200
199
|
if not default_exceptions:
|
|
201
200
|
return None
|
|
202
201
|
exception_schema = default_exceptions[0].type
|
|
203
202
|
if isinstance(exception_schema, ModelType):
|
|
204
|
-
return exception_schema.type_annotation(skip_quote=True)
|
|
203
|
+
return exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
|
|
205
204
|
return None if self.code_model.options["models-mode"] == "dpg" else "'object'"
|
|
206
205
|
|
|
207
206
|
@property
|
|
@@ -469,7 +468,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
469
468
|
file_import.add_submodule_import(relative_path, "_deserialize_xml", ImportType.LOCAL)
|
|
470
469
|
elif self.need_deserialize:
|
|
471
470
|
file_import.add_submodule_import(relative_path, "_deserialize", ImportType.LOCAL)
|
|
472
|
-
if self.default_error_deserialization or self.non_default_errors:
|
|
471
|
+
if self.default_error_deserialization(serialize_namespace) or self.non_default_errors:
|
|
473
472
|
file_import.add_submodule_import(relative_path, "_failsafe_deserialize", ImportType.LOCAL)
|
|
474
473
|
return file_import
|
|
475
474
|
|
|
@@ -173,7 +173,7 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
|
|
|
173
173
|
serialize_namespace, module_name="_utils.model_base"
|
|
174
174
|
)
|
|
175
175
|
file_import.merge(self.item_type.imports(**kwargs))
|
|
176
|
-
if self.default_error_deserialization or self.need_deserialize:
|
|
176
|
+
if self.default_error_deserialization(serialize_namespace) or self.need_deserialize:
|
|
177
177
|
file_import.add_submodule_import(relative_path, "_deserialize", ImportType.LOCAL)
|
|
178
178
|
return file_import
|
|
179
179
|
|
|
@@ -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"),
|
|
@@ -231,9 +252,10 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
231
252
|
if self.keep_version_file and file == "setup.py" and not self.code_model.options["azure-arm"]:
|
|
232
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,60 +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 (
|
|
491
|
-
self.code_model.options["no-namespace-folders"]
|
|
492
|
-
and not self.code_model.options["multiapi"]
|
|
493
|
-
and not self.code_model.options["azure-arm"]
|
|
494
|
-
):
|
|
495
|
-
# when output folder contains parts different from the namespace, we fall back to current folder directly.
|
|
496
|
-
# (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md)
|
|
497
|
-
return Path(".")
|
|
498
|
-
return self.exec_path_compensation / Path(*namespace.split("."))
|
|
499
|
-
|
|
500
511
|
# pylint: disable=line-too-long
|
|
501
512
|
@property
|
|
502
513
|
def sample_additional_folder(self) -> Path:
|
|
@@ -515,8 +526,8 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
515
526
|
return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
|
|
516
527
|
return Path("")
|
|
517
528
|
|
|
518
|
-
def _serialize_and_write_sample(self, env: Environment
|
|
519
|
-
out_path =
|
|
529
|
+
def _serialize_and_write_sample(self, env: Environment):
|
|
530
|
+
out_path = Path("./generated_samples")
|
|
520
531
|
for client in self.code_model.clients:
|
|
521
532
|
for op_group in client.operation_groups:
|
|
522
533
|
for operation in op_group.operations:
|
|
@@ -548,9 +559,9 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
548
559
|
log_error = f"error happens in sample {file}: {e}"
|
|
549
560
|
_LOGGER.error(log_error)
|
|
550
561
|
|
|
551
|
-
def _serialize_and_write_test(self, env: Environment
|
|
562
|
+
def _serialize_and_write_test(self, env: Environment):
|
|
552
563
|
self.code_model.for_test = True
|
|
553
|
-
out_path =
|
|
564
|
+
out_path = Path("./generated_tests")
|
|
554
565
|
general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
|
|
555
566
|
self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
|
|
556
567
|
if not self.code_model.options["azure-arm"]:
|