@autorest/python 6.0.1 → 6.1.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/ChangeLog.md +26 -0
- package/README.md +21 -2
- package/autorest/__init__.py +39 -14
- package/autorest/_utils.py +56 -0
- package/autorest/black/__init__.py +15 -8
- package/autorest/codegen/__init__.py +14 -21
- package/autorest/codegen/_utils.py +14 -0
- package/autorest/codegen/models/__init__.py +10 -1
- package/autorest/codegen/models/model_type.py +12 -1
- package/autorest/codegen/serializers/__init__.py +20 -15
- package/autorest/codegen/serializers/builder_serializer.py +1 -3
- package/autorest/codegen/templates/README.md.jinja2 +3 -3
- package/autorest/codegen/templates/setup.py.jinja2 +1 -2
- package/autorest/jsonrpc/server.py +7 -1
- package/autorest/m2r/__init__.py +11 -4
- package/autorest/m4reformatter/__init__.py +2 -1
- package/autorest/multiapi/__init__.py +5 -6
- package/autorest/multiapi/serializers/__init__.py +5 -3
- package/autorest/multiclient/__init__.py +50 -0
- package/autorest/multiclient/templates/init.py.jinja2 +8 -0
- package/autorest/multiclient/templates/version.py.jinja2 +8 -0
- package/autorest/postprocess/__init__.py +1 -1
- package/autorest/preprocess/__init__.py +13 -3
- package/autorest/preprocess/helpers.py +0 -30
- package/install.py +3 -3
- package/package.json +1 -1
- package/prepare.py +2 -2
- package/requirements.txt +8 -9
- package/run-python3.js +2 -2
- package/setup.py +2 -3
- package/start.py +2 -2
- package/autorest/multiapi/serializers/multiapi_serializer.py +0 -121
package/ChangeLog.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 2022-07-20 - 6.1.0
|
|
4
|
+
|
|
5
|
+
| Library | Min Version |
|
|
6
|
+
| ----------------------------------------------------------------------- | ----------- |
|
|
7
|
+
| `@autorest/core` | `3.8.1` |
|
|
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.0` |
|
|
13
|
+
|
|
14
|
+
**New Features**
|
|
15
|
+
|
|
16
|
+
- Add new plugin `MultiClient` and new flag `--multiclientscript` to handle package structure of multi client #1328
|
|
17
|
+
|
|
18
|
+
**Bug Fixes**
|
|
19
|
+
|
|
20
|
+
- Fallback unrecognized type as string to avoid a fatal error. #1341
|
|
21
|
+
- Fix regression in default namespace for SDKs generated without `--namespace` flag #1354
|
|
22
|
+
|
|
23
|
+
**Other Changes**
|
|
24
|
+
|
|
25
|
+
- Generated code no longer supports Python 3.6 #1353
|
|
26
|
+
- Order json input and response template entries by whether they are required or not #1335
|
|
27
|
+
- Reduce extreme amount of `black` logs when running in `--debug` mode to just log errors
|
|
28
|
+
|
|
3
29
|
### 2022-06-29 - 6.0.1
|
|
4
30
|
|
|
5
31
|
| Library | Min Version |
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
|
|
23
23
|
|
|
24
24
|
#### Python code gen
|
|
25
25
|
|
|
26
|
-
```yaml !$(multiapiscript)
|
|
26
|
+
```yaml !$(multiapiscript) && !$(multiclientscript)
|
|
27
27
|
# default values for version tolerant and black
|
|
28
28
|
black: true
|
|
29
29
|
```
|
|
@@ -43,7 +43,7 @@ modelerfour:
|
|
|
43
43
|
allow-no-input: true
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
```yaml !$(multiapiscript)
|
|
46
|
+
```yaml !$(multiapiscript) && !$(multiclientscript)
|
|
47
47
|
pass-thru:
|
|
48
48
|
- model-deduplicator
|
|
49
49
|
- subset-reducer
|
|
@@ -161,6 +161,25 @@ scope-postprocess/emitter:
|
|
|
161
161
|
output-artifact: python-files
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
# Multi Client pipeline
|
|
165
|
+
|
|
166
|
+
```yaml $(multiclientscript)
|
|
167
|
+
pipeline:
|
|
168
|
+
python/multiclientscript:
|
|
169
|
+
scope: multiclientscript
|
|
170
|
+
output-artifact: python-files
|
|
171
|
+
|
|
172
|
+
python/multiclientscript/emitter:
|
|
173
|
+
input: multiclientscript
|
|
174
|
+
scope: scope-multiclientscript/emitter
|
|
175
|
+
|
|
176
|
+
scope-multiclientscript/emitter:
|
|
177
|
+
input-artifact: python-files
|
|
178
|
+
output-uri-expr: $key
|
|
179
|
+
|
|
180
|
+
output-artifact: python-files
|
|
181
|
+
```
|
|
182
|
+
|
|
164
183
|
# Help
|
|
165
184
|
|
|
166
185
|
```yaml
|
package/autorest/__init__.py
CHANGED
|
@@ -19,21 +19,33 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class ReaderAndWriter:
|
|
22
|
-
def __init__(self, **kwargs: Any) -> None:
|
|
23
|
-
|
|
22
|
+
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
|
|
23
|
+
self.output_folder = Path(output_folder)
|
|
24
|
+
self.options = kwargs
|
|
24
25
|
|
|
25
26
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
26
27
|
"""How does one read a file in cadl?"""
|
|
27
|
-
|
|
28
|
+
# make path relative to output folder
|
|
29
|
+
try:
|
|
30
|
+
with open(self.output_folder / Path(path), "r") as fd:
|
|
31
|
+
return fd.read()
|
|
32
|
+
except FileNotFoundError:
|
|
33
|
+
return ""
|
|
28
34
|
|
|
29
35
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
30
36
|
"""How does writing work in cadl?"""
|
|
31
|
-
|
|
37
|
+
file_folder = Path(filename).parent
|
|
38
|
+
if not Path.is_dir(self.output_folder / file_folder):
|
|
39
|
+
Path.mkdir(self.output_folder / file_folder, parents=True)
|
|
40
|
+
with open(self.output_folder / Path(filename), "w") as fd:
|
|
41
|
+
fd.write(file_content)
|
|
32
42
|
|
|
33
43
|
|
|
34
44
|
class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
35
|
-
def __init__(
|
|
36
|
-
|
|
45
|
+
def __init__(
|
|
46
|
+
self, *, output_folder: Union[str, Path], autorestapi: AutorestAPI
|
|
47
|
+
) -> None:
|
|
48
|
+
super().__init__(output_folder=output_folder)
|
|
37
49
|
self._autorestapi = autorestapi
|
|
38
50
|
|
|
39
51
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
@@ -49,10 +61,6 @@ class Plugin(ReaderAndWriter, ABC):
|
|
|
49
61
|
:param autorestapi: An autorest API instance
|
|
50
62
|
"""
|
|
51
63
|
|
|
52
|
-
def __init__(self, **kwargs: Any) -> None:
|
|
53
|
-
super().__init__(**kwargs)
|
|
54
|
-
self.options: Dict[str, Any] = {}
|
|
55
|
-
|
|
56
64
|
@abstractmethod
|
|
57
65
|
def process(self) -> bool:
|
|
58
66
|
"""The plugin process.
|
|
@@ -67,8 +75,10 @@ class Plugin(ReaderAndWriter, ABC):
|
|
|
67
75
|
class PluginAutorest(Plugin, ReaderAndWriterAutorest):
|
|
68
76
|
"""For our Autorest plugins, we want to take autorest api as input as options, then pass it to the Plugin"""
|
|
69
77
|
|
|
70
|
-
def __init__(
|
|
71
|
-
|
|
78
|
+
def __init__(
|
|
79
|
+
self, autorestapi: AutorestAPI, *, output_folder: Union[str, Path]
|
|
80
|
+
) -> None:
|
|
81
|
+
super().__init__(autorestapi=autorestapi, output_folder=output_folder)
|
|
72
82
|
self.options = self.get_options()
|
|
73
83
|
|
|
74
84
|
@abstractmethod
|
|
@@ -79,15 +89,24 @@ class PluginAutorest(Plugin, ReaderAndWriterAutorest):
|
|
|
79
89
|
class YamlUpdatePlugin(Plugin):
|
|
80
90
|
"""A plugin that update the YAML as input."""
|
|
81
91
|
|
|
92
|
+
def get_yaml(self) -> Dict[str, Any]:
|
|
93
|
+
# cadl file doesn't have to be relative to output folder
|
|
94
|
+
with open(self.options["cadl_file"], "r") as fd:
|
|
95
|
+
return yaml.safe_load(fd.read())
|
|
96
|
+
|
|
97
|
+
def write_yaml(self, yaml_string: str) -> None:
|
|
98
|
+
with open(self.options["cadl_file"], "w") as fd:
|
|
99
|
+
fd.write(yaml_string)
|
|
100
|
+
|
|
82
101
|
def process(self) -> bool:
|
|
83
102
|
# List the input file, should be only one
|
|
84
|
-
yaml_data =
|
|
103
|
+
yaml_data = self.get_yaml()
|
|
85
104
|
|
|
86
105
|
self.update_yaml(yaml_data)
|
|
87
106
|
|
|
88
107
|
yaml_string = yaml.safe_dump(yaml_data)
|
|
89
108
|
|
|
90
|
-
self.
|
|
109
|
+
self.write_yaml(yaml_string)
|
|
91
110
|
return True
|
|
92
111
|
|
|
93
112
|
@abstractmethod
|
|
@@ -103,6 +122,12 @@ class YamlUpdatePlugin(Plugin):
|
|
|
103
122
|
class YamlUpdatePluginAutorest( # pylint: disable=abstract-method
|
|
104
123
|
YamlUpdatePlugin, PluginAutorest
|
|
105
124
|
):
|
|
125
|
+
def get_yaml(self) -> Dict[str, Any]:
|
|
126
|
+
return yaml.safe_load(self.read_file("code-model-v4-no-tags.yaml"))
|
|
127
|
+
|
|
128
|
+
def write_yaml(self, yaml_string: str) -> None:
|
|
129
|
+
self.write_file("code-model-v4-no-tags.yaml", yaml_string)
|
|
130
|
+
|
|
106
131
|
def get_options(self) -> Dict[str, Any]:
|
|
107
132
|
inputs = self._autorestapi.list_inputs()
|
|
108
133
|
_LOGGER.debug("Possible Inputs: %s", inputs)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
import re
|
|
7
|
+
import argparse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def to_snake_case(name: str) -> str:
|
|
11
|
+
def replace_upper_characters(m) -> str:
|
|
12
|
+
match_str = m.group().lower()
|
|
13
|
+
if m.start() > 0 and name[m.start() - 1] == "_":
|
|
14
|
+
# we are good if a '_' already exists
|
|
15
|
+
return match_str
|
|
16
|
+
# the first letter should not have _
|
|
17
|
+
prefix = "_" if m.start() > 0 else ""
|
|
18
|
+
|
|
19
|
+
# we will add an extra _ if there are multiple upper case chars together
|
|
20
|
+
next_non_upper_case_char_location = m.start() + len(match_str)
|
|
21
|
+
if (
|
|
22
|
+
len(match_str) > 2
|
|
23
|
+
and len(name) - next_non_upper_case_char_location > 1
|
|
24
|
+
and name[next_non_upper_case_char_location].isalpha()
|
|
25
|
+
):
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
prefix
|
|
29
|
+
+ match_str[: len(match_str) - 1]
|
|
30
|
+
+ "_"
|
|
31
|
+
+ match_str[len(match_str) - 1]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return prefix + match_str
|
|
35
|
+
|
|
36
|
+
return re.sub("[A-Z]+", replace_upper_characters, name)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_args(need_cadl_file: bool = True):
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description="Run mypy against target folder. Add a local custom plugin to the path prior to execution. "
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--output-folder",
|
|
45
|
+
dest="output_folder",
|
|
46
|
+
help="Output folder for generated SDK",
|
|
47
|
+
required=True,
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--cadl-file",
|
|
51
|
+
dest="cadl_file",
|
|
52
|
+
help="Serialized cadl file",
|
|
53
|
+
required=need_cadl_file,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return parser.parse_args()
|
|
@@ -10,8 +10,9 @@ from typing import Any, Dict
|
|
|
10
10
|
import black
|
|
11
11
|
|
|
12
12
|
from .. import Plugin, PluginAutorest
|
|
13
|
+
from .._utils import parse_args
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
logging.getLogger("blib2to3").setLevel(logging.ERROR)
|
|
15
16
|
|
|
16
17
|
_BLACK_MODE = black.Mode()
|
|
17
18
|
_BLACK_MODE.line_length = 120
|
|
@@ -20,12 +21,12 @@ _BLACK_MODE.line_length = 120
|
|
|
20
21
|
class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
|
|
21
22
|
def __init__(self, **kwargs):
|
|
22
23
|
super().__init__(**kwargs)
|
|
23
|
-
|
|
24
|
-
if
|
|
25
|
-
|
|
26
|
-
if os.name == "nt" and
|
|
27
|
-
|
|
28
|
-
self.output_folder = Path(
|
|
24
|
+
output_folder = self.options.get("output_folder", str(self.output_folder))
|
|
25
|
+
if output_folder.startswith("file:"):
|
|
26
|
+
output_folder = output_folder[5:]
|
|
27
|
+
if os.name == "nt" and output_folder.startswith("///"):
|
|
28
|
+
output_folder = output_folder[3:]
|
|
29
|
+
self.output_folder = Path(output_folder)
|
|
29
30
|
|
|
30
31
|
def process(self) -> bool:
|
|
31
32
|
# apply format_file on every file in the output folder
|
|
@@ -54,4 +55,10 @@ class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
|
|
|
54
55
|
|
|
55
56
|
class BlackScriptPluginAutorest(BlackScriptPlugin, PluginAutorest):
|
|
56
57
|
def get_options(self) -> Dict[str, Any]:
|
|
57
|
-
return {"
|
|
58
|
+
return {"output_folder": self._autorestapi.get_value("outputFolderUri")}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
# CADL pipeline will call this
|
|
63
|
+
args = parse_args(need_cadl_file=False)
|
|
64
|
+
BlackScriptPlugin(output_folder=args.output_folder).process()
|
|
@@ -4,19 +4,20 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
import logging
|
|
7
|
-
import sys
|
|
8
7
|
from typing import Dict, Any, Union, cast
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
import yaml
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
from .. import Plugin, PluginAutorest
|
|
13
|
+
from .._utils import parse_args
|
|
14
14
|
from .models.client import Client, Config
|
|
15
15
|
from .models.code_model import CodeModel
|
|
16
16
|
from .models import build_type
|
|
17
17
|
from .models.request_builder import get_request_builder
|
|
18
18
|
from .models.operation_group import OperationGroup
|
|
19
19
|
from .serializers import JinjaSerializer, JinjaSerializerAutorest
|
|
20
|
+
from ._utils import DEFAULT_HEADER_TEXT
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _build_convenience_layer(yaml_data: Dict[str, Any], code_model: CodeModel) -> None:
|
|
@@ -163,7 +164,7 @@ class CodeGenerator(Plugin):
|
|
|
163
164
|
def _build_code_model_options(self) -> Dict[str, Any]:
|
|
164
165
|
"""Build en options dict from the user input while running autorest."""
|
|
165
166
|
azure_arm = self.options.get("azure-arm", False)
|
|
166
|
-
license_header = self.options
|
|
167
|
+
license_header = self.options.get("header-text", DEFAULT_HEADER_TEXT)
|
|
167
168
|
if license_header:
|
|
168
169
|
license_header = license_header.replace("\n", "\n# ")
|
|
169
170
|
license_header = (
|
|
@@ -236,12 +237,12 @@ class CodeGenerator(Plugin):
|
|
|
236
237
|
return options
|
|
237
238
|
|
|
238
239
|
def get_yaml(self) -> Dict[str, Any]:
|
|
239
|
-
# cadl
|
|
240
|
-
|
|
240
|
+
# cadl file doesn't have to be relative to output folder
|
|
241
|
+
with open(self.options["cadl_file"], "r") as fd:
|
|
242
|
+
return yaml.safe_load(fd.read())
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return JinjaSerializer(code_model)
|
|
244
|
+
def get_serializer(self, code_model: CodeModel):
|
|
245
|
+
return JinjaSerializer(code_model, output_folder=self.output_folder)
|
|
245
246
|
|
|
246
247
|
def process(self) -> bool:
|
|
247
248
|
# List the input file, should be only one
|
|
@@ -351,20 +352,12 @@ class CodeGeneratorAutorest(CodeGenerator, PluginAutorest):
|
|
|
351
352
|
return yaml.safe_load(file_content)
|
|
352
353
|
|
|
353
354
|
def get_serializer(self, code_model: CodeModel): # type: ignore
|
|
354
|
-
return JinjaSerializerAutorest(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
def main(yaml_model_file: str) -> None:
|
|
358
|
-
from ..jsonrpc.localapi import ( # pylint: disable=import-outside-toplevel
|
|
359
|
-
LocalAutorestAPI,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
code_generator = CodeGeneratorAutorest(
|
|
363
|
-
autorestapi=LocalAutorestAPI(reachable_files=[yaml_model_file])
|
|
364
|
-
)
|
|
365
|
-
if not code_generator.process():
|
|
366
|
-
raise SystemExit("Process didn't finish gracefully")
|
|
355
|
+
return JinjaSerializerAutorest(
|
|
356
|
+
self._autorestapi, code_model, output_folder=self.output_folder
|
|
357
|
+
)
|
|
367
358
|
|
|
368
359
|
|
|
369
360
|
if __name__ == "__main__":
|
|
370
|
-
|
|
361
|
+
# CADL pipeline will call this
|
|
362
|
+
args = parse_args()
|
|
363
|
+
CodeGenerator(output_folder=args.output_folder, cadl_file=args.cadl_file).process()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
DEFAULT_HEADER_TEXT = (
|
|
8
|
+
"# --------------------------------------------------------------------------\n"
|
|
9
|
+
"# Copyright (c) Microsoft Corporation. All rights reserved.\n"
|
|
10
|
+
"# Licensed under the MIT License. See License.txt in the project root for license information.\n"
|
|
11
|
+
"# Code generated by Microsoft (R) Python Code Generator.\n"
|
|
12
|
+
"# Changes may cause incorrect behavior and will be lost if the code is regenerated.\n"
|
|
13
|
+
"# --------------------------------------------------------------------------"
|
|
14
|
+
)
|
|
@@ -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
|
+
import logging
|
|
6
7
|
from typing import Any, Dict, Union
|
|
7
8
|
from .base_model import BaseModel
|
|
8
9
|
from .code_model import CodeModel
|
|
@@ -140,6 +141,7 @@ TYPE_TO_OBJECT = {
|
|
|
140
141
|
"any-object": AnyObjectType,
|
|
141
142
|
"unixtime": UnixTimeType,
|
|
142
143
|
}
|
|
144
|
+
_LOGGER = logging.getLogger(__name__)
|
|
143
145
|
|
|
144
146
|
|
|
145
147
|
def build_type(yaml_data: Dict[str, Any], code_model: CodeModel) -> BaseType:
|
|
@@ -155,7 +157,14 @@ def build_type(yaml_data: Dict[str, Any], code_model: CodeModel) -> BaseType:
|
|
|
155
157
|
code_model.types_map[yaml_id] = response
|
|
156
158
|
response.fill_instance_from_yaml(yaml_data, code_model)
|
|
157
159
|
else:
|
|
158
|
-
|
|
160
|
+
object_type = yaml_data.get("type", None)
|
|
161
|
+
if object_type is None:
|
|
162
|
+
_LOGGER.warning(
|
|
163
|
+
'Unrecognized definition type "%s" is found, falling back it as "string"! ',
|
|
164
|
+
yaml_data["type"],
|
|
165
|
+
)
|
|
166
|
+
object_type = "string"
|
|
167
|
+
response = TYPE_TO_OBJECT[object_type].from_yaml(yaml_data, code_model) # type: ignore
|
|
159
168
|
code_model.types_map[yaml_id] = response
|
|
160
169
|
return response
|
|
161
170
|
|
|
@@ -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 collections import OrderedDict
|
|
6
7
|
from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast
|
|
7
8
|
|
|
8
9
|
from autorest.codegen.models.utils import add_to_pylint_disable
|
|
@@ -153,7 +154,17 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
153
154
|
# once we've finished, we want to reset created_json_template_representation to false
|
|
154
155
|
# so we can call it again
|
|
155
156
|
self._created_json_template_representation = False
|
|
156
|
-
|
|
157
|
+
optional_keys = [
|
|
158
|
+
f'"{p.rest_api_name}"'
|
|
159
|
+
for p in self.properties
|
|
160
|
+
if getattr(p, "optional", False)
|
|
161
|
+
]
|
|
162
|
+
return OrderedDict(
|
|
163
|
+
sorted(
|
|
164
|
+
representation.items(),
|
|
165
|
+
key=lambda item: f"{1 if item[0] in optional_keys else 0}{item[0]}",
|
|
166
|
+
)
|
|
167
|
+
)
|
|
157
168
|
|
|
158
169
|
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
159
170
|
is_polymorphic_subtype = (
|
|
@@ -3,7 +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 typing import Dict, List, Optional, Any, Union
|
|
6
|
+
from typing import Dict, List, Optional, Any, Union, cast
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from jinja2 import PackageLoader, Environment, FileSystemLoader, StrictUndefined
|
|
9
9
|
from autorest.codegen.models.operation_group import OperationGroup
|
|
@@ -40,8 +40,10 @@ _REGENERATE_FILES = {"setup.py", "MANIFEST.in"}
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
43
|
-
def __init__(
|
|
44
|
-
|
|
43
|
+
def __init__(
|
|
44
|
+
self, code_model: CodeModel, *, output_folder: Union[str, Path], **kwargs: Any
|
|
45
|
+
) -> None:
|
|
46
|
+
super().__init__(output_folder=output_folder, **kwargs)
|
|
45
47
|
self.code_model = code_model
|
|
46
48
|
|
|
47
49
|
@property
|
|
@@ -133,7 +135,7 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
133
135
|
if self.read_file(namespace_path / Path("models.py")):
|
|
134
136
|
self.write_file(
|
|
135
137
|
namespace_path / Path("models.py"),
|
|
136
|
-
self.read_file(namespace_path / Path("models.py")),
|
|
138
|
+
cast(str, self.read_file(namespace_path / Path("models.py"))),
|
|
137
139
|
)
|
|
138
140
|
|
|
139
141
|
if self.code_model.options["package_mode"]:
|
|
@@ -150,7 +152,9 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
150
152
|
self.write_file(output_name, render_result)
|
|
151
153
|
|
|
152
154
|
def _prepare_params() -> Dict[Any, Any]:
|
|
153
|
-
package_parts = package_name.split("-")[
|
|
155
|
+
package_parts = (self.code_model.options["package_name"] or "").split("-")[
|
|
156
|
+
:-1
|
|
157
|
+
]
|
|
154
158
|
token_cred = isinstance(
|
|
155
159
|
getattr(self.code_model.credential, "type", None), TokenCredentialType
|
|
156
160
|
)
|
|
@@ -177,14 +181,7 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
177
181
|
params.update(self.code_model.package_dependency)
|
|
178
182
|
return params
|
|
179
183
|
|
|
180
|
-
|
|
181
|
-
self.code_model.options["package_name"]
|
|
182
|
-
or self.code_model.client.name.lower()
|
|
183
|
-
)
|
|
184
|
-
count = package_name.count("-") + 1
|
|
185
|
-
for _ in range(count):
|
|
186
|
-
out_path = out_path / Path("..")
|
|
187
|
-
|
|
184
|
+
out_path = out_path / Path("../" * (self.code_model.namespace.count(".") + 1))
|
|
188
185
|
if self.code_model.options["package_mode"] in ("dataplane", "mgmtplane"):
|
|
189
186
|
env = Environment(
|
|
190
187
|
loader=PackageLoader("autorest.codegen", "templates"),
|
|
@@ -484,5 +481,13 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
484
481
|
|
|
485
482
|
|
|
486
483
|
class JinjaSerializerAutorest(JinjaSerializer, ReaderAndWriterAutorest):
|
|
487
|
-
def __init__(
|
|
488
|
-
|
|
484
|
+
def __init__(
|
|
485
|
+
self,
|
|
486
|
+
autorestapi: AutorestAPI,
|
|
487
|
+
code_model: CodeModel,
|
|
488
|
+
*,
|
|
489
|
+
output_folder: Union[str, Path],
|
|
490
|
+
) -> None:
|
|
491
|
+
super().__init__(
|
|
492
|
+
autorestapi=autorestapi, code_model=code_model, output_folder=output_folder
|
|
493
|
+
)
|
|
@@ -79,9 +79,7 @@ def _improve_json_string(template_representation: str) -> Any:
|
|
|
79
79
|
|
|
80
80
|
def _json_dumps_template(template_representation: Any) -> Any:
|
|
81
81
|
# only for template use, since it wraps everything in strings
|
|
82
|
-
return _improve_json_string(
|
|
83
|
-
json.dumps(template_representation, sort_keys=True, indent=4)
|
|
84
|
-
)
|
|
82
|
+
return _improve_json_string(json.dumps(template_representation, indent=4))
|
|
85
83
|
|
|
86
84
|
|
|
87
85
|
def _get_polymorphic_subtype_template(polymorphic_subtype: ModelType) -> List[str]:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Microsoft Azure SDK for Python
|
|
3
3
|
|
|
4
4
|
This is the Microsoft {{package_pprint_name}} Client Library.
|
|
5
|
-
This package has been tested with Python 3.
|
|
5
|
+
This package has been tested with Python 3.7+.
|
|
6
6
|
For a more complete view of Azure libraries, see the [azure sdk python release](https://aka.ms/azsdk/python/all).
|
|
7
7
|
|
|
8
8
|
# Usage
|
|
@@ -17,7 +17,7 @@ Additional code samples for different Azure services are available at [Samples R
|
|
|
17
17
|
|
|
18
18
|
If you encounter any bugs or have suggestions, please file an issue in the
|
|
19
19
|
[Issues](https://github.com/Azure/azure-sdk-for-python/issues)
|
|
20
|
-
section of the project.
|
|
20
|
+
section of the project.
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|

|
|
@@ -35,7 +35,7 @@ python -m pip install {{ package_name }}
|
|
|
35
35
|
|
|
36
36
|
#### Prequisites
|
|
37
37
|
|
|
38
|
-
- Python 3.
|
|
38
|
+
- Python 3.7 or later is required to use this package.
|
|
39
39
|
- You need an [Azure subscription][azure_sub] to use this package.
|
|
40
40
|
- An existing {{ package_pprint_name }} instance.
|
|
41
41
|
|
|
@@ -54,7 +54,6 @@ setup(
|
|
|
54
54
|
"Programming Language :: Python",
|
|
55
55
|
"Programming Language :: Python :: 3 :: Only",
|
|
56
56
|
"Programming Language :: Python :: 3",
|
|
57
|
-
"Programming Language :: Python :: 3.6",
|
|
58
57
|
"Programming Language :: Python :: 3.7",
|
|
59
58
|
"Programming Language :: Python :: 3.8",
|
|
60
59
|
"Programming Language :: Python :: 3.9",
|
|
@@ -94,7 +93,7 @@ setup(
|
|
|
94
93
|
{% endif %}
|
|
95
94
|
],
|
|
96
95
|
{% if package_mode %}
|
|
97
|
-
python_requires=">=3.
|
|
96
|
+
python_requires=">=3.7",
|
|
98
97
|
{% else %}
|
|
99
98
|
long_description="""\
|
|
100
99
|
{{ code_model.client.description }}
|
|
@@ -26,6 +26,7 @@ def GetPluginNames():
|
|
|
26
26
|
"black",
|
|
27
27
|
"multiapiscript",
|
|
28
28
|
"postprocess",
|
|
29
|
+
"multiclientscript",
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
|
|
@@ -56,11 +57,16 @@ def Process(plugin_name: str, session_id: str) -> bool:
|
|
|
56
57
|
from ..black import BlackScriptPluginAutorest as PluginToLoad # type: ignore
|
|
57
58
|
elif plugin_name == "multiapiscript":
|
|
58
59
|
from ..multiapi import MultiApiScriptPluginAutorest as PluginToLoad # type: ignore
|
|
60
|
+
elif plugin_name == "multiclientscript":
|
|
61
|
+
from ..multiclient import MultiClientPluginAutorest as PluginToLoad # type: ignore
|
|
59
62
|
else:
|
|
60
63
|
_LOGGER.fatal("Unknown plugin name %s", plugin_name)
|
|
61
64
|
raise RuntimeError(f"Unknown plugin name {plugin_name}")
|
|
62
65
|
|
|
63
|
-
plugin = PluginToLoad(
|
|
66
|
+
plugin = PluginToLoad(
|
|
67
|
+
autorestapi=stdstream_connection,
|
|
68
|
+
output_folder=stdstream_connection.get_value("output-folder"),
|
|
69
|
+
)
|
|
64
70
|
|
|
65
71
|
try:
|
|
66
72
|
_LOGGER.debug("Starting plugin %s", PluginToLoad.__name__)
|
package/autorest/m2r/__init__.py
CHANGED
|
@@ -8,20 +8,21 @@
|
|
|
8
8
|
import logging
|
|
9
9
|
from typing import Any, Dict, Set
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import m2r2
|
|
12
12
|
|
|
13
13
|
from .. import YamlUpdatePluginAutorest, YamlUpdatePlugin
|
|
14
|
+
from .._utils import parse_args
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
_LOGGER = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class AutorestRender(
|
|
20
|
+
class AutorestRender(m2r2.RestRenderer):
|
|
20
21
|
"""Redefine the concept of inline HTML in the renderer, we don't want to define a new format
|
|
21
22
|
in the description/summary.
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
|
-
def inline_html(self, html: str) -> str:
|
|
25
|
+
def inline_html(self, html: str) -> str: # pylint: disable=no-self-use
|
|
25
26
|
"""Do not render inline HTML with a role definition."""
|
|
26
27
|
return f":code:`{html}`"
|
|
27
28
|
|
|
@@ -55,7 +56,7 @@ class M2R(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
55
56
|
def convert_to_rst(string_to_convert: str) -> str:
|
|
56
57
|
"""Convert that string from MD to RST."""
|
|
57
58
|
try:
|
|
58
|
-
return
|
|
59
|
+
return m2r2.convert(string_to_convert, renderer=AutorestRender()).strip()
|
|
59
60
|
except Exception: # pylint: disable=broad-except
|
|
60
61
|
return string_to_convert
|
|
61
62
|
|
|
@@ -63,3 +64,9 @@ class M2R(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
63
64
|
class M2RAutorest(YamlUpdatePluginAutorest, M2R):
|
|
64
65
|
def get_options(self) -> Dict[str, Any]:
|
|
65
66
|
return {}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
# CADL pipeline will call this
|
|
71
|
+
args = parse_args()
|
|
72
|
+
M2R(output_folder=args.output_folder, cadl_file=args.cadl_file).process()
|
|
@@ -11,6 +11,7 @@ import copy
|
|
|
11
11
|
import logging
|
|
12
12
|
from typing import Callable, Dict, Any, Iterable, List, Optional, Set
|
|
13
13
|
|
|
14
|
+
from .._utils import to_snake_case
|
|
14
15
|
from .. import YamlUpdatePluginAutorest
|
|
15
16
|
|
|
16
17
|
JSON_REGEXP = re.compile(r"^(application|text)/(.+\+)?json$")
|
|
@@ -1100,7 +1101,7 @@ class M4Reformatter(
|
|
|
1100
1101
|
if yaml_data.get("globalParameters")
|
|
1101
1102
|
else "",
|
|
1102
1103
|
"namespace": self._autorestapi.get_value("namespace")
|
|
1103
|
-
or yaml_data["
|
|
1104
|
+
or to_snake_case(yaml_data["info"]["title"].replace(" ", "")),
|
|
1104
1105
|
}
|
|
1105
1106
|
|
|
1106
1107
|
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
|
|
@@ -41,7 +41,7 @@ class MultiApiScriptPluginAutorest(MultiApiScriptPlugin, PluginAutorest):
|
|
|
41
41
|
return MultiAPIAutorest(
|
|
42
42
|
autorestapi=self._autorestapi,
|
|
43
43
|
input_package_name=self.options.get("package-name"),
|
|
44
|
-
output_folder=self.
|
|
44
|
+
output_folder=self.output_folder,
|
|
45
45
|
user_specified_default_api=self.options.get("default-api"),
|
|
46
46
|
no_async=self.options.get("no-async", False),
|
|
47
47
|
)
|
|
@@ -49,7 +49,6 @@ class MultiApiScriptPluginAutorest(MultiApiScriptPlugin, PluginAutorest):
|
|
|
49
49
|
def get_options(self) -> Dict[str, Any]:
|
|
50
50
|
options = {
|
|
51
51
|
"package-name": self._autorestapi.get_value("package-name"),
|
|
52
|
-
"output-folder": self._autorestapi.get_value("output-folder"),
|
|
53
52
|
"default-api": self._autorestapi.get_value("default-api"),
|
|
54
53
|
"no-async": self._autorestapi.get_value("no-async"),
|
|
55
54
|
}
|
|
@@ -66,15 +65,13 @@ class MultiAPI(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
66
65
|
user_specified_default_api: Optional[str] = None,
|
|
67
66
|
**kwargs: Any,
|
|
68
67
|
) -> None:
|
|
69
|
-
super().__init__(**kwargs)
|
|
68
|
+
super().__init__(output_folder=Path(output_folder).resolve(), **kwargs)
|
|
70
69
|
if input_package_name is None:
|
|
71
70
|
raise ValueError(
|
|
72
71
|
"package-name is required, either provide it as args or check your readme configuration"
|
|
73
72
|
)
|
|
74
73
|
self.input_package_name = input_package_name
|
|
75
74
|
_LOGGER.debug("Received package name %s", input_package_name)
|
|
76
|
-
|
|
77
|
-
self.output_folder = Path(output_folder).resolve()
|
|
78
75
|
_LOGGER.debug("Received output-folder %s", output_folder)
|
|
79
76
|
self.output_package_name: str = ""
|
|
80
77
|
self.no_async = no_async
|
|
@@ -194,4 +191,6 @@ class MultiAPIAutorest(MultiAPI, ReaderAndWriterAutorest):
|
|
|
194
191
|
|
|
195
192
|
@property
|
|
196
193
|
def serializer(self) -> MultiAPISerializer:
|
|
197
|
-
return MultiAPISerializerAutorest(
|
|
194
|
+
return MultiAPISerializerAutorest(
|
|
195
|
+
self._autorestapi, output_folder=self.output_folder
|
|
196
|
+
)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Optional
|
|
7
|
+
from typing import Any, Optional, Union
|
|
8
8
|
from jinja2 import PackageLoader, Environment
|
|
9
9
|
|
|
10
10
|
from .import_serializer import FileImportSerializer
|
|
@@ -122,5 +122,7 @@ class MultiAPISerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
class MultiAPISerializerAutorest(MultiAPISerializer, ReaderAndWriterAutorest):
|
|
125
|
-
def __init__(
|
|
126
|
-
|
|
125
|
+
def __init__(
|
|
126
|
+
self, autorestapi: AutorestAPI, *, output_folder: Union[str, Path]
|
|
127
|
+
) -> None:
|
|
128
|
+
super().__init__(autorestapi=autorestapi, output_folder=output_folder)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from jinja2 import Environment, PackageLoader
|
|
10
|
+
from .. import Plugin, PluginAutorest
|
|
11
|
+
|
|
12
|
+
_LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MultiClientPlugin(Plugin): # pylint: disable=abstract-method
|
|
16
|
+
def process(self) -> bool:
|
|
17
|
+
_LOGGER.info("Generating files for multi client")
|
|
18
|
+
|
|
19
|
+
env = Environment(
|
|
20
|
+
loader=PackageLoader("autorest.multiclient", "templates"),
|
|
21
|
+
keep_trailing_newline=True,
|
|
22
|
+
line_statement_prefix="##",
|
|
23
|
+
line_comment_prefix="###",
|
|
24
|
+
trim_blocks=True,
|
|
25
|
+
lstrip_blocks=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# __init__.py
|
|
29
|
+
template = env.get_template("init.py.jinja2")
|
|
30
|
+
self.write_file(Path("__init__.py"), template.render())
|
|
31
|
+
|
|
32
|
+
# _version.py
|
|
33
|
+
template = env.get_template("version.py.jinja2")
|
|
34
|
+
self.write_file(
|
|
35
|
+
Path("_version.py"),
|
|
36
|
+
template.render(
|
|
37
|
+
package_version=self.options.get("package-version") or "1.0.0b1"
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# py.typed
|
|
42
|
+
self.write_file(Path("py.typed"), "# Marker file for PEP 561.")
|
|
43
|
+
|
|
44
|
+
_LOGGER.info("Generating Done for multi client!")
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MultiClientPluginAutorest(MultiClientPlugin, PluginAutorest):
|
|
49
|
+
def get_options(self) -> Dict[str, Any]:
|
|
50
|
+
return {"package-version": self._autorestapi.get_value("package-version")}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
# Code generated by Microsoft (R) AutoRest Code Generator.
|
|
5
|
+
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
6
|
+
# --------------------------------------------------------------------------
|
|
7
|
+
from ._version import VERSION
|
|
8
|
+
__version__ = VERSION
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
# --------------------------------------------------------------------------
|
|
3
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
5
|
+
# license information.
|
|
6
|
+
# --------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
VERSION = "{{ package_version }}"
|
|
@@ -66,7 +66,7 @@ class PostProcessPlugin(Plugin): # pylint: disable=abstract-method
|
|
|
66
66
|
init_file = next(d for d in dir.iterdir() if d.name == "__init__.py")
|
|
67
67
|
# we don't care about pkgutil inits, we skip over them
|
|
68
68
|
file_content = self.read_file(init_file.relative_to(self.output_folder))
|
|
69
|
-
if
|
|
69
|
+
if "pkgutil" not in file_content:
|
|
70
70
|
return dir, namespace
|
|
71
71
|
except StopIteration:
|
|
72
72
|
pass
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
"""The preprocessing autorest plugin.
|
|
7
7
|
"""
|
|
8
8
|
from typing import Callable, Dict, Any, List, Optional
|
|
9
|
-
from
|
|
9
|
+
from .._utils import to_snake_case
|
|
10
|
+
from .helpers import pad_reserved_words, add_redefined_builtin_info
|
|
10
11
|
from .python_mappings import PadType
|
|
11
12
|
|
|
12
|
-
from .. import YamlUpdatePlugin,
|
|
13
|
+
from .. import YamlUpdatePlugin, YamlUpdatePluginAutorest
|
|
14
|
+
from .._utils import parse_args
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
def _remove_paging_maxpagesize(yaml_data: Dict[str, Any]) -> None:
|
|
@@ -230,10 +232,18 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
230
232
|
self.update_operation_groups(yaml_data)
|
|
231
233
|
|
|
232
234
|
|
|
233
|
-
class PreProcessPluginAutorest(
|
|
235
|
+
class PreProcessPluginAutorest(YamlUpdatePluginAutorest, PreProcessPlugin):
|
|
234
236
|
def get_options(self) -> Dict[str, Any]:
|
|
235
237
|
options = {
|
|
236
238
|
"version-tolerant": self._autorestapi.get_boolean_value("version-tolerant"),
|
|
237
239
|
"azure-arm": self._autorestapi.get_boolean_value("azure-arm"),
|
|
238
240
|
}
|
|
239
241
|
return {k: v for k, v in options.items() if v is not None}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
# CADL pipeline will call this
|
|
246
|
+
args = parse_args()
|
|
247
|
+
PreProcessPlugin(
|
|
248
|
+
output_folder=args.output_folder, cadl_file=args.cadl_file
|
|
249
|
+
).process()
|
|
@@ -4,39 +4,9 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
from typing import Any, Dict
|
|
7
|
-
import re
|
|
8
7
|
from .python_mappings import PadType, RESERVED_WORDS, REDEFINED_BUILTINS
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
def to_snake_case(name: str) -> str:
|
|
12
|
-
def replace_upper_characters(m) -> str:
|
|
13
|
-
match_str = m.group().lower()
|
|
14
|
-
if m.start() > 0 and name[m.start() - 1] == "_":
|
|
15
|
-
# we are good if a '_' already exists
|
|
16
|
-
return match_str
|
|
17
|
-
# the first letter should not have _
|
|
18
|
-
prefix = "_" if m.start() > 0 else ""
|
|
19
|
-
|
|
20
|
-
# we will add an extra _ if there are multiple upper case chars together
|
|
21
|
-
next_non_upper_case_char_location = m.start() + len(match_str)
|
|
22
|
-
if (
|
|
23
|
-
len(match_str) > 2
|
|
24
|
-
and len(name) - next_non_upper_case_char_location > 1
|
|
25
|
-
and name[next_non_upper_case_char_location].isalpha()
|
|
26
|
-
):
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
prefix
|
|
30
|
-
+ match_str[: len(match_str) - 1]
|
|
31
|
-
+ "_"
|
|
32
|
-
+ match_str[len(match_str) - 1]
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
return prefix + match_str
|
|
36
|
-
|
|
37
|
-
return re.sub("[A-Z]+", replace_upper_characters, name)
|
|
38
|
-
|
|
39
|
-
|
|
40
10
|
def pad_reserved_words(name: str, pad_type: PadType):
|
|
41
11
|
# we want to pad hidden variables as well
|
|
42
12
|
if not name:
|
package/install.py
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# license information.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
import sys
|
|
9
|
-
if not sys.version_info >= (3,
|
|
10
|
-
raise Exception("Autorest for Python extension requires Python 3.
|
|
9
|
+
if not sys.version_info >= (3, 7, 0):
|
|
10
|
+
raise Exception("Autorest for Python extension requires Python 3.7 at least")
|
|
11
11
|
|
|
12
12
|
try:
|
|
13
13
|
import pip
|
|
@@ -20,7 +20,7 @@ except ImportError:
|
|
|
20
20
|
raise Exception("Your Python installation doesn't have venv available")
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
# Now we have pip and Py >= 3.
|
|
23
|
+
# Now we have pip and Py >= 3.7, go to work
|
|
24
24
|
|
|
25
25
|
import subprocess
|
|
26
26
|
from pathlib import Path
|
package/package.json
CHANGED
package/prepare.py
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# license information.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
import sys
|
|
9
|
-
if not sys.version_info >= (3,
|
|
10
|
-
raise Exception("Autorest for Python extension requires Python 3.
|
|
9
|
+
if not sys.version_info >= (3, 7, 0):
|
|
10
|
+
raise Exception("Autorest for Python extension requires Python 3.7 at least")
|
|
11
11
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
import venv
|
package/requirements.txt
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
black==
|
|
2
|
-
click==8.
|
|
3
|
-
docutils==0.18
|
|
4
|
-
Jinja2==3.
|
|
1
|
+
black==22.6.0
|
|
2
|
+
click==8.1.3
|
|
3
|
+
docutils==0.18
|
|
4
|
+
Jinja2==3.1.2
|
|
5
5
|
json-rpc==1.13.0
|
|
6
|
-
|
|
7
|
-
MarkupSafe==2.
|
|
6
|
+
m2r2==0.3.2
|
|
7
|
+
MarkupSafe==2.1.1
|
|
8
8
|
mistune==0.8.4
|
|
9
9
|
mypy-extensions==0.4.3
|
|
10
10
|
pathspec==0.9.0
|
|
11
|
-
platformdirs==2.
|
|
11
|
+
platformdirs==2.5.2
|
|
12
12
|
PyYAML==6.0
|
|
13
|
-
tomli==
|
|
14
|
-
typing-extensions==4.0.1
|
|
13
|
+
tomli==2.0.1
|
package/run-python3.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
const cp = require("child_process");
|
|
10
10
|
const extension = require("@autorest/system-requirements");
|
|
11
11
|
|
|
12
|
-
async function runPython3(scriptName,
|
|
13
|
-
const command = await extension.patchPythonPath(["python", scriptName,
|
|
12
|
+
async function runPython3(scriptName, ...args) {
|
|
13
|
+
const command = await extension.patchPythonPath(["python", scriptName, ...args], { version: ">=3.7", environmentVariable: "AUTOREST_PYTHON_EXE" });
|
|
14
14
|
cp.execSync(command.join(" "), {
|
|
15
15
|
stdio: [0, 1, 2]
|
|
16
16
|
});
|
package/setup.py
CHANGED
|
@@ -36,7 +36,6 @@ setup(
|
|
|
36
36
|
'Development Status :: 4 - Beta',
|
|
37
37
|
'Programming Language :: Python',
|
|
38
38
|
'Programming Language :: Python :: 3',
|
|
39
|
-
'Programming Language :: Python :: 3.6',
|
|
40
39
|
'Programming Language :: Python :: 3.7',
|
|
41
40
|
'Programming Language :: Python :: 3.8',
|
|
42
41
|
'License :: OSI Approved :: MIT License',
|
|
@@ -48,8 +47,8 @@ setup(
|
|
|
48
47
|
"json-rpc",
|
|
49
48
|
"Jinja2 >= 2.11", # I need "include" and auto-context + blank line are not indented by default
|
|
50
49
|
"pyyaml",
|
|
51
|
-
"
|
|
52
|
-
"m2r",
|
|
50
|
+
"m2r2",
|
|
53
51
|
"black",
|
|
52
|
+
"docutils<0.19", # m2r2 fails with docutils 0.19
|
|
54
53
|
],
|
|
55
54
|
)
|
package/start.py
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# license information.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
import sys
|
|
9
|
-
if not sys.version_info >= (3,
|
|
10
|
-
raise Exception("Autorest for Python extension requires Python 3.
|
|
9
|
+
if not sys.version_info >= (3, 7, 0):
|
|
10
|
+
raise Exception("Autorest for Python extension requires Python 3.7 at least")
|
|
11
11
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
import venv
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# -------------------------------------------------------------------------
|
|
2
|
-
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
-
# license information.
|
|
5
|
-
# --------------------------------------------------------------------------
|
|
6
|
-
from typing import Any, Dict
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from jinja2 import Environment, PackageLoader
|
|
9
|
-
|
|
10
|
-
from ...jsonrpc import AutorestAPI
|
|
11
|
-
from ... import ReaderAndWriter, ReaderAndWriterAutorest
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class MultiAPISerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
15
|
-
def __init__(
|
|
16
|
-
self,
|
|
17
|
-
conf: Dict[str, Any],
|
|
18
|
-
async_mode: bool,
|
|
19
|
-
service_client_filename: str,
|
|
20
|
-
**kwargs: Any
|
|
21
|
-
):
|
|
22
|
-
super().__init__(**kwargs)
|
|
23
|
-
self.conf = conf
|
|
24
|
-
self.async_mode = async_mode
|
|
25
|
-
self.service_client_filename = service_client_filename
|
|
26
|
-
self.env = Environment(
|
|
27
|
-
loader=PackageLoader("autorest.multiapi", "templates"),
|
|
28
|
-
keep_trailing_newline=True,
|
|
29
|
-
line_statement_prefix="##",
|
|
30
|
-
line_comment_prefix="###",
|
|
31
|
-
trim_blocks=True,
|
|
32
|
-
lstrip_blocks=True,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
def _get_file_path(self, filename: str) -> Path:
|
|
36
|
-
if self.async_mode:
|
|
37
|
-
return Path("aio") / filename
|
|
38
|
-
return Path(filename)
|
|
39
|
-
|
|
40
|
-
def serialize(self):
|
|
41
|
-
self.write_file(
|
|
42
|
-
self._get_file_path("__init__.py"), self.serialize_multiapi_init()
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
service_client_filename_with_py_extension = self.service_client_filename + ".py"
|
|
46
|
-
self.write_file(
|
|
47
|
-
self._get_file_path(service_client_filename_with_py_extension),
|
|
48
|
-
self.serialize_multiapi_client(),
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
configuration_filename = "_configuration.py"
|
|
52
|
-
self.write_file(
|
|
53
|
-
self._get_file_path(configuration_filename),
|
|
54
|
-
self.serialize_multiapi_config(),
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
operation_mixins_filename = "_operations_mixin.py"
|
|
58
|
-
if self.conf["mixin_operations"]:
|
|
59
|
-
self.write_file(
|
|
60
|
-
self._get_file_path(operation_mixins_filename),
|
|
61
|
-
self.serialize_multiapi_operation_mixins(),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
if self.read_file("_version.py"):
|
|
65
|
-
self.write_file("_version.py", self.read_file("_version.py"))
|
|
66
|
-
elif self.read_file("version.py"):
|
|
67
|
-
self.write_file("_version.py", self.read_file("version.py"))
|
|
68
|
-
else:
|
|
69
|
-
self.write_file(Path("_version.py"), self.serialize_multiapi_version())
|
|
70
|
-
|
|
71
|
-
# don't erase patch file
|
|
72
|
-
if self.read_file("_patch.py"):
|
|
73
|
-
self.write_file("_patch.py", self.read_file("_patch.py"))
|
|
74
|
-
|
|
75
|
-
self.write_file(Path("models.py"), self.serialize_multiapi_models())
|
|
76
|
-
|
|
77
|
-
self.write_file(Path("py.typed"), "# Marker file for PEP 561.")
|
|
78
|
-
|
|
79
|
-
def serialize_multiapi_init(self) -> str:
|
|
80
|
-
template = self.env.get_template("multiapi_init.py.jinja2")
|
|
81
|
-
return template.render(
|
|
82
|
-
service_client_filename=self.service_client_filename,
|
|
83
|
-
client_name=self.conf["client_name"],
|
|
84
|
-
async_mode=self.async_mode,
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
def serialize_multiapi_client(self) -> str:
|
|
88
|
-
template = self.env.get_template("multiapi_service_client.py.jinja2")
|
|
89
|
-
return template.render(**self.conf, async_mode=self.async_mode)
|
|
90
|
-
|
|
91
|
-
def serialize_multiapi_config(self) -> str:
|
|
92
|
-
template = self.env.get_template("multiapi_config.py.jinja2")
|
|
93
|
-
return template.render(**self.conf, async_mode=self.async_mode)
|
|
94
|
-
|
|
95
|
-
def serialize_multiapi_models(self) -> str:
|
|
96
|
-
template = self.env.get_template("multiapi_models.py.jinja2")
|
|
97
|
-
return template.render(**self.conf)
|
|
98
|
-
|
|
99
|
-
def serialize_multiapi_version(self) -> str:
|
|
100
|
-
template = self.env.get_template("multiapi_version.py.jinja2")
|
|
101
|
-
return template.render()
|
|
102
|
-
|
|
103
|
-
def serialize_multiapi_operation_mixins(self) -> str:
|
|
104
|
-
template = self.env.get_template("multiapi_operations_mixin.py.jinja2")
|
|
105
|
-
return template.render(**self.conf, async_mode=self.async_mode)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class MultiAPISerializerAutorest(MultiAPISerializer, ReaderAndWriterAutorest):
|
|
109
|
-
def __init__(
|
|
110
|
-
self,
|
|
111
|
-
autorestapi: AutorestAPI,
|
|
112
|
-
conf: Dict[str, Any],
|
|
113
|
-
async_mode: bool,
|
|
114
|
-
service_client_filename: str,
|
|
115
|
-
):
|
|
116
|
-
super().__init__(
|
|
117
|
-
autorestapi=autorestapi,
|
|
118
|
-
conf=conf,
|
|
119
|
-
async_mode=async_mode,
|
|
120
|
-
service_client_filename=service_client_filename,
|
|
121
|
-
)
|