@autorest/python 5.16.0 → 5.17.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 +43 -3
- package/README.md +30 -4
- package/autorest/__init__.py +1 -1
- package/autorest/codegen/__init__.py +48 -209
- package/autorest/codegen/models/__init__.py +116 -83
- package/autorest/codegen/models/base_builder.py +49 -88
- package/autorest/codegen/models/base_model.py +1 -1
- package/autorest/codegen/models/{base_schema.py → base_type.py} +56 -40
- package/autorest/codegen/models/client.py +157 -48
- package/autorest/codegen/models/code_model.py +108 -254
- package/autorest/codegen/models/combined_type.py +107 -0
- package/autorest/codegen/models/{constant_schema.py → constant_type.py} +49 -40
- package/autorest/codegen/models/credential_types.py +224 -0
- package/autorest/codegen/models/{dictionary_schema.py → dictionary_type.py} +41 -31
- package/autorest/codegen/models/enum_type.py +195 -0
- package/autorest/codegen/models/imports.py +23 -0
- package/autorest/codegen/models/list_type.py +134 -0
- package/autorest/codegen/models/lro_operation.py +77 -156
- package/autorest/codegen/models/lro_paging_operation.py +28 -11
- package/autorest/codegen/models/model_type.py +239 -0
- package/autorest/codegen/models/operation.py +303 -269
- package/autorest/codegen/models/operation_group.py +48 -89
- package/autorest/codegen/models/paging_operation.py +80 -123
- package/autorest/codegen/models/parameter.py +289 -396
- package/autorest/codegen/models/parameter_list.py +348 -360
- package/autorest/codegen/models/primitive_types.py +544 -0
- package/autorest/codegen/models/property.py +109 -139
- package/autorest/codegen/models/request_builder.py +105 -88
- package/autorest/codegen/models/request_builder_parameter.py +112 -100
- package/autorest/codegen/models/response.py +325 -0
- package/autorest/codegen/models/utils.py +12 -19
- package/autorest/codegen/serializers/__init__.py +46 -37
- package/autorest/codegen/serializers/builder_serializer.py +604 -1146
- package/autorest/codegen/serializers/client_serializer.py +83 -88
- package/autorest/codegen/serializers/general_serializer.py +5 -64
- package/autorest/codegen/serializers/import_serializer.py +7 -4
- package/autorest/codegen/serializers/metadata_serializer.py +15 -104
- package/autorest/codegen/serializers/model_base_serializer.py +40 -32
- package/autorest/codegen/serializers/model_generic_serializer.py +8 -6
- package/autorest/codegen/serializers/model_init_serializer.py +2 -4
- package/autorest/codegen/serializers/model_python3_serializer.py +22 -16
- package/autorest/codegen/serializers/operation_groups_serializer.py +4 -13
- package/autorest/codegen/serializers/parameter_serializer.py +174 -0
- package/autorest/codegen/serializers/request_builders_serializer.py +12 -29
- package/autorest/codegen/serializers/utils.py +0 -142
- package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
- package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -7
- package/autorest/codegen/templates/config.py.jinja2 +13 -13
- package/autorest/codegen/templates/enum.py.jinja2 +4 -4
- package/autorest/codegen/templates/enum_container.py.jinja2 +1 -1
- package/autorest/codegen/templates/init.py.jinja2 +2 -2
- package/autorest/codegen/templates/lro_operation.py.jinja2 +4 -1
- package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +4 -1
- package/autorest/codegen/templates/metadata.json.jinja2 +33 -33
- package/autorest/codegen/templates/model.py.jinja2 +23 -24
- package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
- package/autorest/codegen/templates/model_init.py.jinja2 +3 -5
- package/autorest/codegen/templates/operation.py.jinja2 +6 -8
- package/autorest/codegen/templates/operation_group.py.jinja2 +7 -7
- package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
- package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
- package/autorest/codegen/templates/paging_operation.py.jinja2 +2 -2
- package/autorest/codegen/templates/request_builder.py.jinja2 +13 -11
- package/autorest/codegen/templates/setup.py.jinja2 +9 -3
- package/autorest/codegen/templates/vendor.py.jinja2 +1 -1
- package/autorest/jsonrpc/server.py +15 -3
- package/autorest/m4reformatter/__init__.py +1108 -0
- package/autorest/multiapi/models/code_model.py +1 -1
- package/autorest/multiapi/serializers/__init__.py +4 -4
- package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
- package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
- package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +3 -3
- package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
- package/autorest/postprocess/__init__.py +202 -0
- package/autorest/postprocess/get_all.py +19 -0
- package/autorest/postprocess/venvtools.py +73 -0
- package/autorest/preprocess/__init__.py +209 -0
- package/autorest/preprocess/helpers.py +54 -0
- package/autorest/{namer → preprocess}/python_mappings.py +21 -16
- package/package.json +2 -2
- package/autorest/codegen/models/credential_model.py +0 -55
- package/autorest/codegen/models/credential_schema.py +0 -95
- package/autorest/codegen/models/credential_schema_policy.py +0 -73
- package/autorest/codegen/models/enum_schema.py +0 -225
- package/autorest/codegen/models/list_schema.py +0 -135
- package/autorest/codegen/models/object_schema.py +0 -303
- package/autorest/codegen/models/primitive_schemas.py +0 -495
- package/autorest/codegen/models/request_builder_parameter_list.py +0 -249
- package/autorest/codegen/models/schema_request.py +0 -55
- package/autorest/codegen/models/schema_response.py +0 -141
- package/autorest/namer/__init__.py +0 -23
- package/autorest/namer/name_converter.py +0 -509
|
@@ -34,7 +34,7 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
|
|
|
34
34
|
self.azure_arm = default_version_metadata["client"]["azure_arm"]
|
|
35
35
|
self.default_version_metadata = default_version_metadata
|
|
36
36
|
self.version_path_to_metadata = version_path_to_metadata
|
|
37
|
-
self.
|
|
37
|
+
self.client = Client(
|
|
38
38
|
self.azure_arm, default_version_metadata, version_path_to_metadata
|
|
39
39
|
)
|
|
40
40
|
self.config = Config(default_version_metadata)
|
|
@@ -18,7 +18,7 @@ __all__ = [
|
|
|
18
18
|
|
|
19
19
|
_FILE_TO_TEMPLATE = {
|
|
20
20
|
"init": "multiapi_init.py.jinja2",
|
|
21
|
-
"
|
|
21
|
+
"client": "multiapi_service_client.py.jinja2",
|
|
22
22
|
"config": "multiapi_config.py.jinja2",
|
|
23
23
|
"models": "multiapi_models.py.jinja2",
|
|
24
24
|
"operations_mixin": "multiapi_operations_mixin.py.jinja2",
|
|
@@ -58,11 +58,11 @@ class MultiAPISerializer(object):
|
|
|
58
58
|
|
|
59
59
|
# serialize service client file
|
|
60
60
|
imports = FileImportSerializer(
|
|
61
|
-
code_model.
|
|
61
|
+
code_model.client.imports(async_mode), is_python3_file=async_mode
|
|
62
62
|
)
|
|
63
63
|
self._autorestapi.write_file(
|
|
64
|
-
_get_file_path(code_model.
|
|
65
|
-
_render_template("
|
|
64
|
+
_get_file_path(code_model.client.filename, async_mode),
|
|
65
|
+
_render_template("client", imports=imports),
|
|
66
66
|
)
|
|
67
67
|
|
|
68
68
|
# serialize config file
|
|
@@ -28,8 +28,8 @@ def __init__(
|
|
|
28
28
|
# --------------------------------------------------------------------------
|
|
29
29
|
{{ imports }}
|
|
30
30
|
|
|
31
|
-
class {{ code_model.
|
|
32
|
-
"""Configuration for {{ code_model.
|
|
31
|
+
class {{ code_model.client.name }}Configuration(Configuration):
|
|
32
|
+
"""Configuration for {{ code_model.client.name }}.
|
|
33
33
|
|
|
34
34
|
Note that all parameters used to create this instance are saved as instance
|
|
35
35
|
attributes.
|
|
@@ -52,7 +52,7 @@ class {{ code_model.service_client.name }}Configuration(Configuration):
|
|
|
52
52
|
raise ValueError("Parameter '{{ parameter.name }}' must not be None.")
|
|
53
53
|
{% endif %}
|
|
54
54
|
{% endfor %}
|
|
55
|
-
super({{ code_model.
|
|
55
|
+
super({{ code_model.client.name }}Configuration, self).__init__(**kwargs)
|
|
56
56
|
|
|
57
57
|
{% for parameter in code_model.global_parameters.parameters %}
|
|
58
58
|
self.{{ parameter.name }} = {{ parameter.name }}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
from .{{ code_model.
|
|
10
|
-
__all__ = ['{{ code_model.
|
|
9
|
+
from .{{ code_model.client.filename }} import {{ code_model.client.name }}
|
|
10
|
+
__all__ = ['{{ code_model.client.name }}']
|
|
11
11
|
{% if not async_mode %}
|
|
12
12
|
|
|
13
13
|
try:
|
|
@@ -14,7 +14,7 @@ from msrest import Serializer, Deserializer
|
|
|
14
14
|
{% endif %}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class {{ code_model.
|
|
17
|
+
class {{ code_model.client.name }}OperationsMixin(object):
|
|
18
18
|
{% for mixin_operation in code_model.operation_mixin_group.mixin_operations %}
|
|
19
19
|
|
|
20
20
|
{{ mixin_operation.signature(async_mode) | indent }} {{ mixin_operation.description(async_mode) | indent(8) }}
|
|
@@ -22,7 +22,7 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
|
|
|
22
22
|
{% for api in mixin_operation.available_apis|sort %}
|
|
23
23
|
{% set if_statement = "if" if loop.first else "elif" %}
|
|
24
24
|
{{ if_statement }} api_version == '{{ code_model.mod_to_api_version[api] }}':
|
|
25
|
-
from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.
|
|
25
|
+
from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.client.name }}OperationsMixin as OperationClass
|
|
26
26
|
{% endfor %}
|
|
27
27
|
else:
|
|
28
28
|
raise ValueError("API version {} does not have operation '{{ mixin_operation.name }}'".format(api_version))
|
|
@@ -30,7 +30,7 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
|
|
|
30
30
|
mixin_instance._client = self._client
|
|
31
31
|
mixin_instance._config = self._config
|
|
32
32
|
mixin_instance._serialize = Serializer(self._models_dict(api_version))
|
|
33
|
-
{% if not code_model.
|
|
33
|
+
{% if not code_model.client.client_side_validation %}
|
|
34
34
|
mixin_instance._serialize.client_side_validation = False
|
|
35
35
|
{% endif %}
|
|
36
36
|
mixin_instance._deserialize = Deserializer(self._models_dict(api_version))
|
|
@@ -43,8 +43,8 @@ class _SDKClient(object):
|
|
|
43
43
|
"""
|
|
44
44
|
pass
|
|
45
45
|
|
|
46
|
-
class {{ code_model.
|
|
47
|
-
"""{{ code_model.
|
|
46
|
+
class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_operations %}{{ code_model.client.name }}OperationsMixin, {% endif %}MultiApiClientMixin, _SDKClient):
|
|
47
|
+
"""{{ code_model.client.description }}
|
|
48
48
|
|
|
49
49
|
This ready contains multiple API versions, to help you deal with all of the Azure clouds
|
|
50
50
|
(Azure Stack, Azure Government, Azure China, etc.).
|
|
@@ -64,13 +64,13 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
64
64
|
:param {{ parameter.name }}: {{ parameter.description(async_mode) }}
|
|
65
65
|
:type {{ parameter.name }}: {{ parameter.docstring_type(async_mode) }}
|
|
66
66
|
{% endfor %}
|
|
67
|
-
{% if code_model.
|
|
67
|
+
{% if code_model.client.has_lro_operations %}
|
|
68
68
|
:keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present.
|
|
69
69
|
{% endif %}
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
72
|
DEFAULT_API_VERSION = '{{ code_model.mod_to_api_version[code_model.default_api_version] }}'
|
|
73
|
-
_PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.
|
|
73
|
+
_PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.client.name }}"
|
|
74
74
|
LATEST_PROFILE = ProfileDefinition({
|
|
75
75
|
_PROFILE_TAG: {
|
|
76
76
|
None: DEFAULT_API_VERSION,
|
|
@@ -82,8 +82,8 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
{{ method_signature()|indent }}
|
|
85
|
-
{% if not code_model.
|
|
86
|
-
{% for parameterized_host_template, api_versions in code_model.
|
|
85
|
+
{% if not code_model.client.host_value %}
|
|
86
|
+
{% for parameterized_host_template, api_versions in code_model.client.parameterized_host_template_to_api_version|dictsort %}
|
|
87
87
|
{% set if_statement = "if" if loop.first else "elif" %}
|
|
88
88
|
{{ if_statement ~ " api_version == '" ~ api_versions|join("' or api_version == '") ~ "'" }}:
|
|
89
89
|
base_url = {{ parameterized_host_template }}
|
|
@@ -91,9 +91,9 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
|
|
|
91
91
|
else:
|
|
92
92
|
raise ValueError("API version {} is not available".format(api_version))
|
|
93
93
|
{% endif %}
|
|
94
|
-
self._config = {{ code_model.
|
|
95
|
-
self._client = {{ async_prefix }}{{ code_model.
|
|
96
|
-
super({{ code_model.
|
|
94
|
+
self._config = {{ code_model.client.name }}Configuration({{ code_model.global_parameters.call }}{{ ", " if code_model.global_parameters.call }}**kwargs)
|
|
95
|
+
self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url=base_url, config=self._config, **kwargs)
|
|
96
|
+
super({{ code_model.client.name }}, self).__init__(
|
|
97
97
|
api_version=api_version,
|
|
98
98
|
profile=profile
|
|
99
99
|
)
|
|
@@ -0,0 +1,202 @@
|
|
|
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 Tuple
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
from venv import EnvBuilder
|
|
11
|
+
import black
|
|
12
|
+
from .venvtools import ExtendedEnvBuilder, python_run
|
|
13
|
+
|
|
14
|
+
from .. import Plugin
|
|
15
|
+
|
|
16
|
+
_BLACK_MODE = black.Mode()
|
|
17
|
+
_BLACK_MODE.line_length = 120
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_file(file: Path, file_content: str) -> str:
|
|
21
|
+
if not file.suffix == ".py":
|
|
22
|
+
return file_content
|
|
23
|
+
try:
|
|
24
|
+
file_content = black.format_file_contents(
|
|
25
|
+
file_content, fast=True, mode=_BLACK_MODE
|
|
26
|
+
)
|
|
27
|
+
except black.NothingChanged:
|
|
28
|
+
pass
|
|
29
|
+
return file_content
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PostProcessPlugin(Plugin):
|
|
33
|
+
def __init__(self, autorestapi):
|
|
34
|
+
super().__init__(autorestapi)
|
|
35
|
+
output_folder_uri = self._autorestapi.get_value("outputFolderUri")
|
|
36
|
+
if output_folder_uri.startswith("file:"):
|
|
37
|
+
output_folder_uri = output_folder_uri[5:]
|
|
38
|
+
if os.name == "nt" and output_folder_uri.startswith("///"):
|
|
39
|
+
output_folder_uri = output_folder_uri[3:]
|
|
40
|
+
self.output_folder = Path(output_folder_uri) # path to where the setup.py is
|
|
41
|
+
self.setup_venv()
|
|
42
|
+
|
|
43
|
+
# set up the venv
|
|
44
|
+
# base folder is where the code starts, i.e. where we
|
|
45
|
+
self.base_folder, self.namespace = self.get_namespace(self.output_folder, "")
|
|
46
|
+
|
|
47
|
+
def setup_venv(self):
|
|
48
|
+
venv_path = self.output_folder / Path(".temp_folder") / Path("temp_venv")
|
|
49
|
+
|
|
50
|
+
if venv_path.exists():
|
|
51
|
+
env_builder = EnvBuilder(with_pip=True)
|
|
52
|
+
self.venv_context = env_builder.ensure_directories(venv_path)
|
|
53
|
+
else:
|
|
54
|
+
env_builder = ExtendedEnvBuilder(with_pip=True)
|
|
55
|
+
env_builder.create(venv_path)
|
|
56
|
+
self.venv_context = env_builder.context
|
|
57
|
+
python_run(
|
|
58
|
+
self.venv_context,
|
|
59
|
+
"pip",
|
|
60
|
+
["install", "-e", str(self.output_folder)],
|
|
61
|
+
directory=self.output_folder,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def get_namespace(self, dir: Path, namespace: str) -> Tuple[Path, str]:
|
|
65
|
+
try:
|
|
66
|
+
init_file = next(d for d in dir.iterdir() if d.name == "__init__.py")
|
|
67
|
+
# we don't care about pkgutil inits, we skip over them
|
|
68
|
+
file_content = self._autorestapi.read_file(
|
|
69
|
+
init_file.relative_to(self.output_folder)
|
|
70
|
+
)
|
|
71
|
+
if not "pkgutil" in file_content:
|
|
72
|
+
return dir, namespace
|
|
73
|
+
except StopIteration:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
# first, see if we can get a folder that has the same name as the current output folder
|
|
78
|
+
start = self.output_folder.stem.split("-")[0]
|
|
79
|
+
next_dir = next(d for d in dir.iterdir() if d.is_dir() and d.name == start)
|
|
80
|
+
except StopIteration:
|
|
81
|
+
invalid_start_chars = [".", "_"]
|
|
82
|
+
invalid_dirs = [
|
|
83
|
+
"swagger",
|
|
84
|
+
"out",
|
|
85
|
+
"tests",
|
|
86
|
+
"samples",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
next_dir = next(
|
|
90
|
+
d
|
|
91
|
+
for d in dir.iterdir()
|
|
92
|
+
if d.is_dir()
|
|
93
|
+
and not str(d).endswith("egg-info")
|
|
94
|
+
and d.name[0] not in invalid_start_chars
|
|
95
|
+
and d.name not in invalid_dirs
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
namespace = f"{namespace}.{next_dir.name}" if namespace else next_dir.name
|
|
99
|
+
return self.get_namespace(next_dir, namespace)
|
|
100
|
+
|
|
101
|
+
def process(self) -> bool:
|
|
102
|
+
folders = [
|
|
103
|
+
f
|
|
104
|
+
for f in self.base_folder.glob("**/*")
|
|
105
|
+
if f.is_dir() and not f.stem.startswith("__")
|
|
106
|
+
]
|
|
107
|
+
# will always have the root
|
|
108
|
+
self.fix_imports_in_init(
|
|
109
|
+
generated_file_name="_client",
|
|
110
|
+
folder_path=self.base_folder,
|
|
111
|
+
namespace=self.namespace,
|
|
112
|
+
)
|
|
113
|
+
try:
|
|
114
|
+
aio_folder = next(f for f in folders if f.stem == "aio")
|
|
115
|
+
self.fix_imports_in_init(
|
|
116
|
+
generated_file_name="_client",
|
|
117
|
+
folder_path=aio_folder,
|
|
118
|
+
namespace=f"{self.namespace}.aio",
|
|
119
|
+
)
|
|
120
|
+
except StopIteration:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
models_folder = next(f for f in folders if f.stem == "models")
|
|
125
|
+
self.fix_imports_in_init(
|
|
126
|
+
generated_file_name="_models",
|
|
127
|
+
folder_path=models_folder,
|
|
128
|
+
namespace=f"{self.namespace}.models",
|
|
129
|
+
)
|
|
130
|
+
except StopIteration:
|
|
131
|
+
pass
|
|
132
|
+
operations_folders = [
|
|
133
|
+
f for f in folders if f.stem in ["operations", "_operations"]
|
|
134
|
+
]
|
|
135
|
+
for operations_folder in operations_folders:
|
|
136
|
+
aio = ".aio" if operations_folder.parent.stem == "aio" else ""
|
|
137
|
+
self.fix_imports_in_init(
|
|
138
|
+
generated_file_name="_operations",
|
|
139
|
+
folder_path=operations_folder,
|
|
140
|
+
namespace=f"{self.namespace}{aio}.{operations_folder.stem}",
|
|
141
|
+
)
|
|
142
|
+
shutil.rmtree(f"{str(self.output_folder)}/.temp_folder")
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
def fix_imports_in_init(
|
|
146
|
+
self, generated_file_name: str, folder_path: Path, namespace: str
|
|
147
|
+
) -> None:
|
|
148
|
+
customized_objects_str = python_run(
|
|
149
|
+
self.venv_context,
|
|
150
|
+
command=[namespace, str(self.output_folder)],
|
|
151
|
+
module="get_all",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if not customized_objects_str:
|
|
155
|
+
return
|
|
156
|
+
customized_objects = {
|
|
157
|
+
k: None for k in customized_objects_str.split(",")
|
|
158
|
+
}.keys() # filter out duplicates
|
|
159
|
+
file = (folder_path / "__init__.py").relative_to(self.output_folder)
|
|
160
|
+
file_content = self._autorestapi.read_file(file)
|
|
161
|
+
added_objs = []
|
|
162
|
+
for obj in customized_objects:
|
|
163
|
+
if f" import {obj}\n" in file_content:
|
|
164
|
+
# means we're overriding a generated model
|
|
165
|
+
file_content = file_content.replace(
|
|
166
|
+
f"from .{generated_file_name} import {obj}\n",
|
|
167
|
+
f"from ._patch import {obj}\n",
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
added_objs.append(obj)
|
|
171
|
+
file_content = file_content.replace(
|
|
172
|
+
"try:\n from ._patch import __all__ as _patch_all\n "
|
|
173
|
+
"from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import"
|
|
174
|
+
"\nexcept ImportError:\n _patch_all = []",
|
|
175
|
+
"",
|
|
176
|
+
)
|
|
177
|
+
file_content = file_content.replace(
|
|
178
|
+
"from ._patch import __all__ as _patch_all", ""
|
|
179
|
+
)
|
|
180
|
+
file_content = file_content.replace(
|
|
181
|
+
"from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import\n",
|
|
182
|
+
"",
|
|
183
|
+
)
|
|
184
|
+
file_content = file_content.replace(
|
|
185
|
+
"__all__.extend([p for p in _patch_all if p not in __all__])", ""
|
|
186
|
+
)
|
|
187
|
+
if added_objs:
|
|
188
|
+
# add import
|
|
189
|
+
patch_sdk_import = "from ._patch import patch_sdk as _patch_sdk"
|
|
190
|
+
imports = "\n".join([f"from ._patch import {obj}" for obj in added_objs])
|
|
191
|
+
if imports:
|
|
192
|
+
replacement = f"{imports}\n{patch_sdk_import}"
|
|
193
|
+
else:
|
|
194
|
+
replacement = patch_sdk_import
|
|
195
|
+
file_content = file_content.replace(patch_sdk_import, replacement)
|
|
196
|
+
# add to __all__
|
|
197
|
+
added_objs_all = "\n".join([f' "{obj}",' for obj in added_objs]) + "\n"
|
|
198
|
+
file_content = file_content.replace(
|
|
199
|
+
"__all__ = [", f"__all__ = [\n{added_objs_all}", 1
|
|
200
|
+
)
|
|
201
|
+
formatted_file = format_file(file, file_content)
|
|
202
|
+
self._autorestapi.write_file(file, formatted_file)
|
|
@@ -0,0 +1,19 @@
|
|
|
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 sys
|
|
7
|
+
import importlib
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main(namespace):
|
|
11
|
+
sdk = importlib.import_module(namespace)
|
|
12
|
+
return sdk._patch.__all__ # pylint: disable=protected-access
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
patched = ",".join(main(sys.argv[1]))
|
|
17
|
+
output_folder = sys.argv[2]
|
|
18
|
+
with open(f"{output_folder}/.temp_folder/patched.txt", "w") as f:
|
|
19
|
+
f.write(patched)
|
|
@@ -0,0 +1,73 @@
|
|
|
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 Optional
|
|
7
|
+
import subprocess
|
|
8
|
+
import venv
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_ROOT_DIR = Path(__file__).parent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExtendedEnvBuilder(venv.EnvBuilder):
|
|
17
|
+
"""An extended env builder which saves the context, to have access
|
|
18
|
+
easily to bin path and such.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, *args, **kwargs):
|
|
22
|
+
self.context = None
|
|
23
|
+
super(ExtendedEnvBuilder, self).__init__(*args, **kwargs)
|
|
24
|
+
|
|
25
|
+
def ensure_directories(self, env_dir):
|
|
26
|
+
self.context = super(ExtendedEnvBuilder, self).ensure_directories(env_dir)
|
|
27
|
+
return self.context
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create(
|
|
31
|
+
env_dir,
|
|
32
|
+
system_site_packages=False,
|
|
33
|
+
clear=False,
|
|
34
|
+
symlinks=False,
|
|
35
|
+
with_pip=False,
|
|
36
|
+
prompt=None,
|
|
37
|
+
):
|
|
38
|
+
"""Create a virtual environment in a directory."""
|
|
39
|
+
builder = ExtendedEnvBuilder(
|
|
40
|
+
system_site_packages=system_site_packages,
|
|
41
|
+
clear=clear,
|
|
42
|
+
symlinks=symlinks,
|
|
43
|
+
with_pip=with_pip,
|
|
44
|
+
prompt=prompt,
|
|
45
|
+
)
|
|
46
|
+
builder.create(env_dir)
|
|
47
|
+
return builder.context
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def python_run( # pylint: disable=inconsistent-return-statements
|
|
51
|
+
venv_context, module, command, directory=_ROOT_DIR, *, error_ok=False
|
|
52
|
+
) -> Optional[str]:
|
|
53
|
+
try:
|
|
54
|
+
cmd_line = [
|
|
55
|
+
venv_context.env_exe,
|
|
56
|
+
"-m",
|
|
57
|
+
module,
|
|
58
|
+
] + command
|
|
59
|
+
print("Executing: {}".format(" ".join(cmd_line)))
|
|
60
|
+
subprocess.run(
|
|
61
|
+
cmd_line,
|
|
62
|
+
cwd=directory,
|
|
63
|
+
check=True,
|
|
64
|
+
stdout=False,
|
|
65
|
+
)
|
|
66
|
+
if module == "get_all":
|
|
67
|
+
with open(f"{command[1]}/.temp_folder/patched.txt", "r") as f:
|
|
68
|
+
return f.read()
|
|
69
|
+
except subprocess.CalledProcessError as err:
|
|
70
|
+
print(err)
|
|
71
|
+
if not error_ok:
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
return None
|
|
@@ -0,0 +1,209 @@
|
|
|
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
|
+
"""The preprocessing autorest plugin.
|
|
7
|
+
"""
|
|
8
|
+
from typing import Callable, Dict, Any, List, Optional
|
|
9
|
+
from .helpers import to_snake_case, pad_reserved_words, add_redefined_builtin_info
|
|
10
|
+
from .python_mappings import PadType
|
|
11
|
+
|
|
12
|
+
from .. import YamlUpdatePlugin
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def update_description(
|
|
16
|
+
description: Optional[str], default_description: str = ""
|
|
17
|
+
) -> str:
|
|
18
|
+
if not description:
|
|
19
|
+
description = default_description
|
|
20
|
+
description.rstrip(" ")
|
|
21
|
+
if description and description[-1] != ".":
|
|
22
|
+
description += "."
|
|
23
|
+
return description
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def update_operation_group_class_name(
|
|
27
|
+
yaml_data: Dict[str, Any], class_name: str
|
|
28
|
+
) -> str:
|
|
29
|
+
if class_name == "":
|
|
30
|
+
return yaml_data["client"]["name"] + "OperationsMixin"
|
|
31
|
+
if class_name == "Operations":
|
|
32
|
+
return "Operations"
|
|
33
|
+
return class_name + "Operations"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def update_parameter(yaml_data: Dict[str, Any]) -> None:
|
|
37
|
+
yaml_data["description"] = update_description(yaml_data["description"])
|
|
38
|
+
if not (
|
|
39
|
+
yaml_data["location"] == "header"
|
|
40
|
+
and yaml_data["clientName"] in ("content_type", "accept")
|
|
41
|
+
):
|
|
42
|
+
yaml_data["clientName"] = pad_reserved_words(
|
|
43
|
+
yaml_data["clientName"].lower(), PadType.PARAMETER
|
|
44
|
+
)
|
|
45
|
+
if yaml_data.get("propertyToParameterName"):
|
|
46
|
+
# need to create a new one with padded keys and values
|
|
47
|
+
yaml_data["propertyToParameterName"] = {
|
|
48
|
+
pad_reserved_words(prop, PadType.PROPERTY)
|
|
49
|
+
.lower(): pad_reserved_words(param_name, PadType.PARAMETER)
|
|
50
|
+
.lower()
|
|
51
|
+
for prop, param_name in yaml_data["propertyToParameterName"].items()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def update_types(yaml_data: List[Dict[str, Any]]) -> None:
|
|
56
|
+
for type in yaml_data:
|
|
57
|
+
for property in type.get("properties", []):
|
|
58
|
+
property["description"] = update_description(property["description"])
|
|
59
|
+
property["clientName"] = pad_reserved_words(
|
|
60
|
+
property["clientName"].lower(), PadType.PROPERTY
|
|
61
|
+
)
|
|
62
|
+
add_redefined_builtin_info(property["clientName"], property)
|
|
63
|
+
if type.get("name"):
|
|
64
|
+
type["description"] = update_description(type["description"], type["name"])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def update_client(yaml_data: Dict[str, Any]) -> None:
|
|
68
|
+
yaml_data["description"] = update_description(
|
|
69
|
+
yaml_data["description"], default_description=yaml_data["name"]
|
|
70
|
+
)
|
|
71
|
+
yaml_data["moduleName"] = to_snake_case(yaml_data["name"].replace(" ", "_"))
|
|
72
|
+
for parameter in yaml_data["parameters"]:
|
|
73
|
+
update_parameter(parameter)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def update_paging_response(yaml_data: Dict[str, Any]) -> None:
|
|
77
|
+
yaml_data["discriminator"] = "paging"
|
|
78
|
+
yaml_data["pagerSync"] = yaml_data.get("pagerSync") or "azure.core.paging.ItemPaged"
|
|
79
|
+
yaml_data["pagerAsync"] = (
|
|
80
|
+
yaml_data.get("pagerAsync") or "azure.core.async_paging.AsyncItemPaged"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class PreProcessPlugin(YamlUpdatePlugin):
|
|
85
|
+
"""Add Python naming information."""
|
|
86
|
+
|
|
87
|
+
def get_operation_updater(
|
|
88
|
+
self, yaml_data: Dict[str, Any]
|
|
89
|
+
) -> Callable[[Dict[str, Any]], None]:
|
|
90
|
+
if yaml_data["discriminator"] == "lropaging":
|
|
91
|
+
return self.update_lro_paging_operation
|
|
92
|
+
if yaml_data["discriminator"] == "lro":
|
|
93
|
+
return self.update_lro_operation
|
|
94
|
+
if yaml_data["discriminator"] == "paging":
|
|
95
|
+
return self.update_paging_operation
|
|
96
|
+
return self.update_operation
|
|
97
|
+
|
|
98
|
+
def update_operation(self, yaml_data: Dict[str, Any]) -> None:
|
|
99
|
+
yaml_data["groupName"] = pad_reserved_words(
|
|
100
|
+
yaml_data["groupName"], PadType.OPERATION_GROUP
|
|
101
|
+
)
|
|
102
|
+
yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
|
|
103
|
+
yaml_data["name"] = yaml_data["name"].lower()
|
|
104
|
+
yaml_data["name"] = pad_reserved_words(yaml_data["name"], PadType.METHOD)
|
|
105
|
+
yaml_data["description"] = update_description(
|
|
106
|
+
yaml_data["description"], yaml_data["name"]
|
|
107
|
+
)
|
|
108
|
+
yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
|
|
109
|
+
for parameter in yaml_data["parameters"]:
|
|
110
|
+
update_parameter(parameter)
|
|
111
|
+
if yaml_data.get("bodyParameter"):
|
|
112
|
+
update_parameter(yaml_data["bodyParameter"])
|
|
113
|
+
for entry in yaml_data["bodyParameter"].get("entries", []):
|
|
114
|
+
update_parameter(entry)
|
|
115
|
+
for overload in yaml_data.get("overloads", []):
|
|
116
|
+
self.update_operation(overload)
|
|
117
|
+
for response in yaml_data.get("responses", []):
|
|
118
|
+
response["discriminator"] = "operation"
|
|
119
|
+
|
|
120
|
+
def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
|
|
121
|
+
azure_arm = self._autorestapi.get_boolean_value("azure-arm", False)
|
|
122
|
+
for response in yaml_data.get("responses", []):
|
|
123
|
+
response["discriminator"] = "lro"
|
|
124
|
+
response["pollerSync"] = (
|
|
125
|
+
response.get("pollerSync") or "azure.core.polling.LROPoller"
|
|
126
|
+
)
|
|
127
|
+
response["pollerAsync"] = (
|
|
128
|
+
response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
|
|
129
|
+
)
|
|
130
|
+
if not response.get("pollingMethodSync"):
|
|
131
|
+
response["pollingMethodSync"] = (
|
|
132
|
+
"azure.mgmt.core.polling.arm_polling.ARMPolling"
|
|
133
|
+
if azure_arm
|
|
134
|
+
else "azure.core.polling.base_polling.LROBasePolling"
|
|
135
|
+
)
|
|
136
|
+
if not response.get("pollingMethodAsync"):
|
|
137
|
+
response["pollingMethodAsync"] = (
|
|
138
|
+
"azure.mgmt.core.polling.async_arm_polling.AsyncARMPolling"
|
|
139
|
+
if azure_arm
|
|
140
|
+
else "azure.core.polling.async_base_polling.AsyncLROBasePolling"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def update_lro_paging_operation(self, yaml_data: Dict[str, Any]) -> None:
|
|
144
|
+
self.update_lro_operation(yaml_data)
|
|
145
|
+
self.update_paging_operation(yaml_data)
|
|
146
|
+
for response in yaml_data.get("responses", []):
|
|
147
|
+
response["discriminator"] = "lropaging"
|
|
148
|
+
|
|
149
|
+
def update_lro_operation(self, yaml_data: Dict[str, Any]) -> None:
|
|
150
|
+
self.update_operation(yaml_data)
|
|
151
|
+
self._update_lro_operation_helper(yaml_data)
|
|
152
|
+
for overload in yaml_data["overloads"]:
|
|
153
|
+
self._update_lro_operation_helper(overload)
|
|
154
|
+
|
|
155
|
+
def update_paging_operation(self, yaml_data: Dict[str, Any]) -> None:
|
|
156
|
+
self.update_operation(yaml_data)
|
|
157
|
+
if not yaml_data.get("pagerSync"):
|
|
158
|
+
yaml_data["pagerSync"] = "azure.core.paging.ItemPaged"
|
|
159
|
+
if not yaml_data.get("pagerAsync"):
|
|
160
|
+
yaml_data["pagerAsync"] = "azure.core.async_paging.AsyncItemPaged"
|
|
161
|
+
returned_response_object = (
|
|
162
|
+
yaml_data["nextOperation"]["responses"][0]
|
|
163
|
+
if yaml_data.get("nextOperation")
|
|
164
|
+
else yaml_data["responses"][0]
|
|
165
|
+
)
|
|
166
|
+
# if we're in version tolerant, hide the paging model
|
|
167
|
+
if self._autorestapi.get_boolean_value("version-tolerant"):
|
|
168
|
+
returned_response_object["type"]["isPublic"] = False
|
|
169
|
+
item_type = next(
|
|
170
|
+
p["type"]["elementType"]
|
|
171
|
+
for p in returned_response_object["type"]["properties"]
|
|
172
|
+
if p["restApiName"] == yaml_data["itemName"]
|
|
173
|
+
)
|
|
174
|
+
if yaml_data.get("nextOperation"):
|
|
175
|
+
yaml_data["nextOperation"]["groupName"] = pad_reserved_words(
|
|
176
|
+
yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP
|
|
177
|
+
)
|
|
178
|
+
yaml_data["nextOperation"]["groupName"] = to_snake_case(
|
|
179
|
+
yaml_data["nextOperation"]["groupName"]
|
|
180
|
+
)
|
|
181
|
+
for response in yaml_data["nextOperation"].get("responses", []):
|
|
182
|
+
update_paging_response(response)
|
|
183
|
+
response["itemType"] = item_type
|
|
184
|
+
for response in yaml_data.get("responses", []):
|
|
185
|
+
update_paging_response(response)
|
|
186
|
+
response["itemType"] = item_type
|
|
187
|
+
for overload in yaml_data.get("overloads", []):
|
|
188
|
+
self.update_paging_operation(overload)
|
|
189
|
+
|
|
190
|
+
def update_operation_groups(self, yaml_data: Dict[str, Any]) -> None:
|
|
191
|
+
operation_groups_yaml_data = yaml_data["operationGroups"]
|
|
192
|
+
for operation_group in operation_groups_yaml_data:
|
|
193
|
+
operation_group["propertyName"] = pad_reserved_words(
|
|
194
|
+
operation_group["propertyName"], PadType.OPERATION_GROUP
|
|
195
|
+
)
|
|
196
|
+
operation_group["propertyName"] = to_snake_case(
|
|
197
|
+
operation_group["propertyName"]
|
|
198
|
+
)
|
|
199
|
+
operation_group["className"] = update_operation_group_class_name(
|
|
200
|
+
yaml_data, operation_group["className"]
|
|
201
|
+
)
|
|
202
|
+
for operation in operation_group["operations"]:
|
|
203
|
+
self.get_operation_updater(operation)(operation)
|
|
204
|
+
|
|
205
|
+
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
|
|
206
|
+
"""Convert in place the YAML str."""
|
|
207
|
+
update_client(yaml_data["client"])
|
|
208
|
+
update_types(yaml_data["types"])
|
|
209
|
+
self.update_operation_groups(yaml_data)
|