@autorest/python 6.1.5 → 6.1.6
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/ChangeLog.md +15 -0
- package/autorest/__init__.py +10 -3
- package/autorest/black/__init__.py +2 -8
- package/autorest/codegen/models/base_builder.py +4 -1
- package/autorest/codegen/models/operation.py +11 -0
- package/autorest/codegen/models/operation_group.py +5 -0
- package/autorest/codegen/models/parameter.py +7 -0
- package/autorest/codegen/serializers/__init__.py +6 -0
- package/autorest/codegen/serializers/builder_serializer.py +22 -2
- package/autorest/codegen/serializers/general_serializer.py +4 -0
- package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
- package/autorest/codegen/templates/validation.py.jinja2 +38 -0
- package/package.json +1 -1
package/ChangeLog.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 2022-09-15 - 6.1.6
|
|
4
|
+
|
|
5
|
+
| Library | Min Version |
|
|
6
|
+
| ----------------------------------------------------------------------- | ----------- |
|
|
7
|
+
| `@autorest/core` | `3.8.4` |
|
|
8
|
+
| `@autorest/modelerfour` | `4.23.5` |
|
|
9
|
+
| `azure-core` dep of generated code | `1.24.0` |
|
|
10
|
+
| `isodate` dep of generated code | `0.6.1` |
|
|
11
|
+
| `msrest` dep of generated code (If generating legacy code) | `0.7.1` |
|
|
12
|
+
| `azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.2` |
|
|
13
|
+
|
|
14
|
+
**Bug Fixes**
|
|
15
|
+
|
|
16
|
+
- Fix `--clear-output-folder` when `--black=true` #1410
|
|
17
|
+
|
|
3
18
|
### 2022-09-06 - 6.1.5
|
|
4
19
|
|
|
5
20
|
| Library | Min Version |
|
package/autorest/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import json
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
10
|
-
from typing import Any, Dict, Union
|
|
10
|
+
from typing import Any, Dict, Union, List
|
|
11
11
|
|
|
12
12
|
import yaml
|
|
13
13
|
|
|
@@ -22,6 +22,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
22
22
|
class ReaderAndWriter:
|
|
23
23
|
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
|
|
24
24
|
self.output_folder = Path(output_folder)
|
|
25
|
+
self._list_file: List[str] = []
|
|
25
26
|
try:
|
|
26
27
|
with open(
|
|
27
28
|
Path(self.output_folder) / Path("..") / Path("python.json"), "r"
|
|
@@ -37,7 +38,7 @@ class ReaderAndWriter:
|
|
|
37
38
|
self.options.update(python_json)
|
|
38
39
|
|
|
39
40
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
40
|
-
"""
|
|
41
|
+
"""Directly reading from disk"""
|
|
41
42
|
# make path relative to output folder
|
|
42
43
|
try:
|
|
43
44
|
with open(self.output_folder / Path(path), "r") as fd:
|
|
@@ -46,13 +47,16 @@ class ReaderAndWriter:
|
|
|
46
47
|
return ""
|
|
47
48
|
|
|
48
49
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
49
|
-
"""
|
|
50
|
+
"""Directly writing to disk"""
|
|
50
51
|
file_folder = Path(filename).parent
|
|
51
52
|
if not Path.is_dir(self.output_folder / file_folder):
|
|
52
53
|
Path.mkdir(self.output_folder / file_folder, parents=True)
|
|
53
54
|
with open(self.output_folder / Path(filename), "w") as fd:
|
|
54
55
|
fd.write(file_content)
|
|
55
56
|
|
|
57
|
+
def list_file(self) -> List[str]:
|
|
58
|
+
return [str(f) for f in self.output_folder.glob("**/*") if f.is_file()]
|
|
59
|
+
|
|
56
60
|
|
|
57
61
|
class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
58
62
|
def __init__(
|
|
@@ -67,6 +71,9 @@ class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
|
67
71
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
68
72
|
return self._autorestapi.write_file(filename, file_content)
|
|
69
73
|
|
|
74
|
+
def list_file(self) -> List[str]:
|
|
75
|
+
return self._autorestapi.list_inputs()
|
|
76
|
+
|
|
70
77
|
|
|
71
78
|
class Plugin(ReaderAndWriter, ABC):
|
|
72
79
|
"""A base class for autorest plugin.
|
|
@@ -30,16 +30,10 @@ class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
|
|
|
30
30
|
|
|
31
31
|
def process(self) -> bool:
|
|
32
32
|
# apply format_file on every file in the output folder
|
|
33
|
-
list(
|
|
34
|
-
map(
|
|
35
|
-
self.format_file,
|
|
36
|
-
[f for f in self.output_folder.glob("**/*") if f.is_file()],
|
|
37
|
-
)
|
|
38
|
-
)
|
|
33
|
+
list(map(self.format_file, [Path(f) for f in self.list_file()]))
|
|
39
34
|
return True
|
|
40
35
|
|
|
41
|
-
def format_file(self,
|
|
42
|
-
file = full_path.relative_to(self.output_folder)
|
|
36
|
+
def format_file(self, file: Path) -> None:
|
|
43
37
|
file_content = self.read_file(file)
|
|
44
38
|
if not file.suffix == ".py":
|
|
45
39
|
self.write_file(file, file_content)
|
|
@@ -31,7 +31,9 @@ if TYPE_CHECKING:
|
|
|
31
31
|
_LOGGER = logging.getLogger(__name__)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
class BaseBuilder(
|
|
34
|
+
class BaseBuilder(
|
|
35
|
+
Generic[ParameterListType], BaseModel
|
|
36
|
+
): # pylint: disable=too-many-instance-attributes
|
|
35
37
|
"""Base class for Operations and Request Builders"""
|
|
36
38
|
|
|
37
39
|
def __init__(
|
|
@@ -56,6 +58,7 @@ class BaseBuilder(Generic[ParameterListType], BaseModel):
|
|
|
56
58
|
self.group_name: str = yaml_data["groupName"]
|
|
57
59
|
self.is_overload: bool = yaml_data["isOverload"]
|
|
58
60
|
self.api_versions: List[str] = yaml_data["apiVersions"]
|
|
61
|
+
self.added_on: Optional[str] = yaml_data.get("addedOn")
|
|
59
62
|
|
|
60
63
|
if code_model.options["version_tolerant"] and yaml_data.get("abstract"):
|
|
61
64
|
_LOGGER.warning(
|
|
@@ -209,6 +209,12 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
209
209
|
file_import.add_submodule_import(
|
|
210
210
|
"typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
|
|
211
211
|
)
|
|
212
|
+
if self.added_on:
|
|
213
|
+
file_import.add_submodule_import(
|
|
214
|
+
f"{'.' if async_mode else ''}.._validation",
|
|
215
|
+
"api_version_validation",
|
|
216
|
+
ImportType.LOCAL,
|
|
217
|
+
)
|
|
212
218
|
return file_import
|
|
213
219
|
|
|
214
220
|
def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
|
|
@@ -244,6 +250,11 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
244
250
|
for kwarg in kwargs_to_pop
|
|
245
251
|
)
|
|
246
252
|
|
|
253
|
+
@property
|
|
254
|
+
def need_validation(self) -> bool:
|
|
255
|
+
"""Whether we need parameter / operation validation. For API version."""
|
|
256
|
+
return bool(self.added_on) or any(p for p in self.parameters if p.added_on)
|
|
257
|
+
|
|
247
258
|
def get_request_builder_import(
|
|
248
259
|
self,
|
|
249
260
|
request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
|
|
@@ -66,6 +66,11 @@ class OperationGroup(BaseModel):
|
|
|
66
66
|
return " # type: ignore"
|
|
67
67
|
return ""
|
|
68
68
|
|
|
69
|
+
@property
|
|
70
|
+
def need_validation(self) -> bool:
|
|
71
|
+
"""Whether any of its operations need validation"""
|
|
72
|
+
return any(o for o in self.operations if o.need_validation)
|
|
73
|
+
|
|
69
74
|
def imports(self, async_mode: bool) -> FileImport:
|
|
70
75
|
file_import = FileImport()
|
|
71
76
|
|
|
@@ -82,6 +82,7 @@ class _ParameterBase(
|
|
|
82
82
|
self.in_flattened_body: bool = self.yaml_data.get("inFlattenedBody", False)
|
|
83
83
|
self.grouper: bool = self.yaml_data.get("grouper", False)
|
|
84
84
|
self.check_client_input: bool = self.yaml_data.get("checkClientInput", False)
|
|
85
|
+
self.added_on: Optional[str] = self.yaml_data.get("addedOn")
|
|
85
86
|
|
|
86
87
|
@property
|
|
87
88
|
def constant(self) -> bool:
|
|
@@ -152,6 +153,12 @@ class _ParameterBase(
|
|
|
152
153
|
)
|
|
153
154
|
if self.optional and self.client_default_value is None:
|
|
154
155
|
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
|
|
156
|
+
if self.added_on:
|
|
157
|
+
file_import.add_submodule_import(
|
|
158
|
+
f"{'.' if async_mode else ''}.._validation",
|
|
159
|
+
"api_version_validation",
|
|
160
|
+
ImportType.LOCAL,
|
|
161
|
+
)
|
|
155
162
|
return file_import
|
|
156
163
|
|
|
157
164
|
@property
|
|
@@ -428,6 +428,12 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
428
428
|
general_serializer.serialize_serialization_file(),
|
|
429
429
|
)
|
|
430
430
|
|
|
431
|
+
if any(og for og in self.code_model.operation_groups if og.need_validation):
|
|
432
|
+
self.write_file(
|
|
433
|
+
namespace_path / Path("_validation.py"),
|
|
434
|
+
general_serializer.serialize_validation_file(),
|
|
435
|
+
)
|
|
436
|
+
|
|
431
437
|
# Write the config file
|
|
432
438
|
if self.code_model.request_builders:
|
|
433
439
|
self.write_file(
|
|
@@ -173,6 +173,22 @@ def _get_json_response_template_to_status_codes(
|
|
|
173
173
|
return retval
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
def _api_version_validation(builder: OperationType) -> str:
|
|
177
|
+
retval: List[str] = []
|
|
178
|
+
if builder.added_on:
|
|
179
|
+
retval.append(f' method_added_on="{builder.added_on}",')
|
|
180
|
+
params_added_on = defaultdict(list)
|
|
181
|
+
for parameter in builder.parameters:
|
|
182
|
+
if parameter.added_on:
|
|
183
|
+
params_added_on[parameter.added_on].append(parameter.client_name)
|
|
184
|
+
if params_added_on:
|
|
185
|
+
retval.append(f" params_added_on={dict(params_added_on)},")
|
|
186
|
+
if retval:
|
|
187
|
+
retval_str = "\n".join(retval)
|
|
188
|
+
return f"@api_version_validation(\n{retval_str}\n)"
|
|
189
|
+
return ""
|
|
190
|
+
|
|
191
|
+
|
|
176
192
|
class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-method
|
|
177
193
|
def __init__(self, code_model: CodeModel, async_mode: bool) -> None:
|
|
178
194
|
self.code_model = code_model
|
|
@@ -590,8 +606,10 @@ class _OperationSerializer(
|
|
|
590
606
|
|
|
591
607
|
def decorators(self, builder: OperationType) -> List[str]:
|
|
592
608
|
"""Decorators for the method"""
|
|
593
|
-
|
|
594
|
-
|
|
609
|
+
retval = super().decorators(builder)
|
|
610
|
+
if _api_version_validation(builder):
|
|
611
|
+
retval.append(_api_version_validation(builder))
|
|
612
|
+
return retval
|
|
595
613
|
|
|
596
614
|
def param_description(
|
|
597
615
|
self, builder: OperationType
|
|
@@ -1138,6 +1156,8 @@ class _PagingOperationSerializer(
|
|
|
1138
1156
|
return ["@overload"]
|
|
1139
1157
|
if self.code_model.options["tracing"] and builder.want_tracing:
|
|
1140
1158
|
retval.append("@distributed_trace")
|
|
1159
|
+
if _api_version_validation(builder):
|
|
1160
|
+
retval.append(_api_version_validation(builder))
|
|
1141
1161
|
return retval
|
|
1142
1162
|
|
|
1143
1163
|
def call_next_link_request_builder(self, builder: PagingOperationType) -> List[str]:
|
|
@@ -120,3 +120,7 @@ class GeneralSerializer:
|
|
|
120
120
|
def serialize_serialization_file(self) -> str:
|
|
121
121
|
template = self.env.get_template("serialization.py.jinja2")
|
|
122
122
|
return template.render(code_model=self.code_model)
|
|
123
|
+
|
|
124
|
+
def serialize_validation_file(self) -> str:
|
|
125
|
+
template = self.env.get_template("validation.py.jinja2")
|
|
126
|
+
return template.render(code_model=self.code_model)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{{ code_model.options['license_header'] }}
|
|
2
|
+
import functools
|
|
3
|
+
|
|
4
|
+
def api_version_validation(**kwargs):
|
|
5
|
+
params_added_on = kwargs.pop("params_added_on", {})
|
|
6
|
+
method_added_on = kwargs.pop("method_added_on", "")
|
|
7
|
+
|
|
8
|
+
def decorator(func):
|
|
9
|
+
@functools.wraps(func)
|
|
10
|
+
def wrapper(*args, **kwargs):
|
|
11
|
+
try:
|
|
12
|
+
# this assumes the client has an _api_version attribute
|
|
13
|
+
client = args[0]
|
|
14
|
+
client_api_version = client._config.api_version # pylint: disable=protected-access
|
|
15
|
+
except AttributeError:
|
|
16
|
+
return func(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
if method_added_on > client_api_version:
|
|
19
|
+
raise ValueError(
|
|
20
|
+
f"'{func.__name__}' is not available in API version "
|
|
21
|
+
f"{client_api_version}. Pass service API version {method_added_on} or newer to your client."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
unsupported = {
|
|
25
|
+
parameter: api_version
|
|
26
|
+
for api_version, parameters in params_added_on.items()
|
|
27
|
+
for parameter in parameters
|
|
28
|
+
if parameter in kwargs and api_version > client_api_version
|
|
29
|
+
}
|
|
30
|
+
if unsupported:
|
|
31
|
+
raise ValueError("".join([
|
|
32
|
+
f"'{param}' is not available in API version {client_api_version}. "
|
|
33
|
+
f"Use service API version {version} or newer.\n"
|
|
34
|
+
for param, version in unsupported.items()
|
|
35
|
+
]))
|
|
36
|
+
return func(*args, **kwargs)
|
|
37
|
+
return wrapper
|
|
38
|
+
return decorator
|