@autorest/python 6.1.0 → 6.1.3
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 +46 -0
- package/README.md +1 -1
- package/autorest/__init__.py +13 -0
- package/autorest/_utils.py +19 -0
- package/autorest/cadlflags/__init__.py +128 -0
- package/autorest/codegen/_utils.py +4 -6
- package/autorest/codegen/models/combined_type.py +4 -0
- package/autorest/codegen/models/model_type.py +6 -0
- package/autorest/codegen/models/parameter.py +5 -1
- package/autorest/codegen/models/property.py +1 -8
- package/autorest/codegen/models/request_builder_parameter.py +1 -1
- package/autorest/codegen/models/response.py +7 -0
- package/autorest/codegen/models/utils.py +0 -3
- package/autorest/codegen/serializers/builder_serializer.py +4 -2
- package/autorest/m4reformatter/__init__.py +12 -37
- package/autorest/multiapi/templates/multiapi_init.py.jinja2 +4 -0
- package/autorest/preprocess/__init__.py +135 -15
- package/package.json +3 -2
- package/run_cadl.py +27 -0
package/ChangeLog.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 2022-08-22 - 6.1.3
|
|
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 circular recursion for lropaging #1400
|
|
17
|
+
|
|
18
|
+
### 2022-08-16 - 6.1.2
|
|
19
|
+
|
|
20
|
+
| Library | Min Version |
|
|
21
|
+
| ----------------------------------------------------------------------- | ----------- |
|
|
22
|
+
| `@autorest/core` | `3.8.1` |
|
|
23
|
+
| `@autorest/modelerfour` | `4.23.5` |
|
|
24
|
+
| `azure-core` dep of generated code | `1.24.0` |
|
|
25
|
+
| `isodate` dep of generated code | `0.6.1` |
|
|
26
|
+
| `msrest` dep of generated code (If generating legacy code) | `0.7.1` |
|
|
27
|
+
| `azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0` |
|
|
28
|
+
|
|
29
|
+
**Bug Fixes**
|
|
30
|
+
|
|
31
|
+
- Correctly document polymorphic page responses #1389
|
|
32
|
+
- Add `__version__` to `__init__.py` for multiapi #1393
|
|
33
|
+
|
|
34
|
+
### 2022-07-20 - 6.1.1
|
|
35
|
+
|
|
36
|
+
| Library | Min Version |
|
|
37
|
+
| ----------------------------------------------------------------------- | ----------- |
|
|
38
|
+
| `@autorest/core` | `3.8.1` |
|
|
39
|
+
| `@autorest/modelerfour` | `4.23.5` |
|
|
40
|
+
| `azure-core` dep of generated code | `1.24.0` |
|
|
41
|
+
| `isodate` dep of generated code | `0.6.1` |
|
|
42
|
+
| `msrest` dep of generated code (If generating legacy code) | `0.7.1` |
|
|
43
|
+
| `azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0` |
|
|
44
|
+
|
|
45
|
+
**Other Changes**
|
|
46
|
+
|
|
47
|
+
- Get skeleton for cadl generation released #1358
|
|
48
|
+
|
|
3
49
|
### 2022-07-20 - 6.1.0
|
|
4
50
|
|
|
5
51
|
| Library | Min Version |
|
package/README.md
CHANGED
package/autorest/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
import logging
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
import json
|
|
8
9
|
from abc import ABC, abstractmethod
|
|
9
10
|
from typing import Any, Dict, Union
|
|
10
11
|
|
|
@@ -21,7 +22,19 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
21
22
|
class ReaderAndWriter:
|
|
22
23
|
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
|
|
23
24
|
self.output_folder = Path(output_folder)
|
|
25
|
+
try:
|
|
26
|
+
with open(
|
|
27
|
+
Path(self.output_folder) / Path("..") / Path("python.json"), "r"
|
|
28
|
+
) as fd:
|
|
29
|
+
python_json = json.load(fd)
|
|
30
|
+
except Exception: # pylint: disable=broad-except
|
|
31
|
+
python_json = {}
|
|
24
32
|
self.options = kwargs
|
|
33
|
+
if python_json:
|
|
34
|
+
_LOGGER.warning(
|
|
35
|
+
"Loading python.json file. This behavior will be depreacted"
|
|
36
|
+
)
|
|
37
|
+
self.options.update(python_json)
|
|
25
38
|
|
|
26
39
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
27
40
|
"""How does one read a file in cadl?"""
|
package/autorest/_utils.py
CHANGED
|
@@ -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 typing import Any, Dict
|
|
6
7
|
import re
|
|
7
8
|
import argparse
|
|
8
9
|
|
|
@@ -54,3 +55,21 @@ def parse_args(need_cadl_file: bool = True):
|
|
|
54
55
|
)
|
|
55
56
|
|
|
56
57
|
return parser.parse_args()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_body_type_for_description(body_parameter: Dict[str, Any]) -> str:
|
|
61
|
+
if body_parameter["type"]["type"] == "binary":
|
|
62
|
+
return "binary"
|
|
63
|
+
if body_parameter["type"]["type"] == "string":
|
|
64
|
+
return "string"
|
|
65
|
+
return "JSON"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# used if we want to get a string / binary type etc
|
|
69
|
+
KNOWN_TYPES: Dict[str, Dict[str, Any]] = {
|
|
70
|
+
"string": {"type": "string"},
|
|
71
|
+
"binary": {"type": "binary"},
|
|
72
|
+
"anydict": {"type": "dict", "elementType": {"type": "any"}},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
JSON_REGEXP = re.compile(r"^(application|text)/(.+\+)?json$")
|
|
@@ -0,0 +1,128 @@
|
|
|
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, List
|
|
8
|
+
from .. import YamlUpdatePlugin
|
|
9
|
+
from .._utils import parse_args
|
|
10
|
+
|
|
11
|
+
_LOGGER = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
OAUTH_TYPE = "OAuth2"
|
|
14
|
+
KEY_TYPE = "Key"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_azure_key_credential(key: str) -> Dict[str, Any]:
|
|
18
|
+
return {
|
|
19
|
+
"type": KEY_TYPE,
|
|
20
|
+
"policy": {"type": "AzureKeyCredentialPolicy", "key": key},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CadlFlags(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
25
|
+
"""A plugin to apply flags from backdoor python.json into cadl yaml file"""
|
|
26
|
+
|
|
27
|
+
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
|
|
28
|
+
"""Convert in place the YAML str."""
|
|
29
|
+
if self.options.get("add-credential"):
|
|
30
|
+
self.update_credential(yaml_data)
|
|
31
|
+
if self.options.get("namespace"):
|
|
32
|
+
yaml_data["client"]["namespace"] = self.options["namespace"]
|
|
33
|
+
if self.options.get("title"):
|
|
34
|
+
yaml_data["client"]["name"] = self.options["title"]
|
|
35
|
+
|
|
36
|
+
def get_credential_scopes_from_flags(self, auth_policy: str) -> List[str]:
|
|
37
|
+
if self.options.get("azure-arm"):
|
|
38
|
+
return ["https://management.azure.com/.default"]
|
|
39
|
+
credential_scopes_temp = self.options.get("credential-scopes")
|
|
40
|
+
credential_scopes = (
|
|
41
|
+
credential_scopes_temp.split(",") if credential_scopes_temp else None
|
|
42
|
+
)
|
|
43
|
+
if self.options.get("credential-scopes", False) and not credential_scopes:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"--credential-scopes takes a list of scopes in comma separated format. "
|
|
46
|
+
"For example: --credential-scopes=https://cognitiveservices.azure.com/.default"
|
|
47
|
+
)
|
|
48
|
+
if not credential_scopes:
|
|
49
|
+
_LOGGER.warning(
|
|
50
|
+
"You have default credential policy %s "
|
|
51
|
+
"but not the --credential-scopes flag set while generating non-management plane code. "
|
|
52
|
+
"This is not recommend because it forces the customer to pass credential scopes "
|
|
53
|
+
"through kwargs if they want to authenticate.",
|
|
54
|
+
auth_policy,
|
|
55
|
+
)
|
|
56
|
+
credential_scopes = []
|
|
57
|
+
return credential_scopes
|
|
58
|
+
|
|
59
|
+
def get_token_credential(self, credential_scopes: List[str]) -> Dict[str, Any]:
|
|
60
|
+
return {
|
|
61
|
+
"type": OAUTH_TYPE,
|
|
62
|
+
"policy": {
|
|
63
|
+
"type": "ARMChallengeAuthenticationPolicy"
|
|
64
|
+
if self.options.get("azure-arm")
|
|
65
|
+
else "BearerTokenCredentialPolicy",
|
|
66
|
+
"credentialScopes": credential_scopes,
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def update_credential_from_flags(self) -> Dict[str, Any]:
|
|
71
|
+
default_auth_policy = (
|
|
72
|
+
"ARMChallengeAuthenticationPolicy"
|
|
73
|
+
if self.options.get("azure-arm")
|
|
74
|
+
else "BearerTokenCredentialPolicy"
|
|
75
|
+
)
|
|
76
|
+
auth_policy = (
|
|
77
|
+
self.options.get("credential-default-policy-type") or default_auth_policy
|
|
78
|
+
)
|
|
79
|
+
credential_scopes = self.get_credential_scopes_from_flags(auth_policy)
|
|
80
|
+
key = self.options.get("credential-key-header-name")
|
|
81
|
+
if auth_policy.lower() in (
|
|
82
|
+
"armchallengeauthenticationpolicy",
|
|
83
|
+
"bearertokencredentialpolicy",
|
|
84
|
+
):
|
|
85
|
+
if key:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
"You have passed in a credential key header name with default credential policy type "
|
|
88
|
+
f"{auth_policy}. This is not allowed, since credential key header "
|
|
89
|
+
"name is tied with AzureKeyCredentialPolicy. Instead, with this policy it is recommend you "
|
|
90
|
+
"pass in --credential-scopes."
|
|
91
|
+
)
|
|
92
|
+
return self.get_token_credential(credential_scopes)
|
|
93
|
+
# Otherwise you have AzureKeyCredentialPolicy
|
|
94
|
+
if self.options.get("credential-scopes"):
|
|
95
|
+
raise ValueError(
|
|
96
|
+
"You have passed in credential scopes with default credential policy type "
|
|
97
|
+
"AzureKeyCredentialPolicy. This is not allowed, since credential scopes is tied with "
|
|
98
|
+
f"{default_auth_policy}. Instead, with this policy "
|
|
99
|
+
"you must pass in --credential-key-header-name."
|
|
100
|
+
)
|
|
101
|
+
if not key:
|
|
102
|
+
key = "api-key"
|
|
103
|
+
_LOGGER.info(
|
|
104
|
+
"Defaulting the AzureKeyCredentialPolicy header's name to 'api-key'"
|
|
105
|
+
)
|
|
106
|
+
return get_azure_key_credential(key)
|
|
107
|
+
|
|
108
|
+
def update_credential(self, yaml_data: Dict[str, Any]) -> None:
|
|
109
|
+
credential_type = self.update_credential_from_flags()
|
|
110
|
+
yaml_data["types"].append(credential_type)
|
|
111
|
+
credential = {
|
|
112
|
+
"type": credential_type,
|
|
113
|
+
"optional": False,
|
|
114
|
+
"description": "Credential needed for the client to connect to Azure.",
|
|
115
|
+
"clientName": "credential",
|
|
116
|
+
"location": "other",
|
|
117
|
+
"restApiName": "credential",
|
|
118
|
+
"implementation": "Client",
|
|
119
|
+
"skipUrlEncoding": True,
|
|
120
|
+
"inOverload": False,
|
|
121
|
+
}
|
|
122
|
+
yaml_data["client"]["parameters"].append(credential)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
# CADL pipeline will call this
|
|
127
|
+
args = parse_args()
|
|
128
|
+
CadlFlags(output_folder=args.output_folder, cadl_file=args.cadl_file).process()
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
|
|
7
7
|
DEFAULT_HEADER_TEXT = (
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"# Changes may cause incorrect behavior and will be lost if the code is regenerated.\n"
|
|
13
|
-
"# --------------------------------------------------------------------------"
|
|
8
|
+
"Copyright (c) Microsoft Corporation. All rights reserved.\n"
|
|
9
|
+
"Licensed under the MIT License. See License.txt in the project root for license information.\n"
|
|
10
|
+
"Code generated by Microsoft (R) Python Code Generator.\n"
|
|
11
|
+
"Changes may cause incorrect behavior and will be lost if the code is regenerated."
|
|
14
12
|
)
|
|
@@ -10,6 +10,7 @@ from .base_type import BaseType
|
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from .code_model import CodeModel
|
|
13
|
+
from .model_type import ModelType
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class CombinedType(BaseType):
|
|
@@ -82,6 +83,9 @@ class CombinedType(BaseType):
|
|
|
82
83
|
"You shouldn't get a JSON template representation of multiple types"
|
|
83
84
|
)
|
|
84
85
|
|
|
86
|
+
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
87
|
+
raise ValueError("You shouldn't get polymorphic subtypes of multiple types")
|
|
88
|
+
|
|
85
89
|
@property
|
|
86
90
|
def instance_check_template(self) -> str:
|
|
87
91
|
"""Template of what an instance check of a variable for this type would look like"""
|
|
@@ -61,6 +61,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
61
61
|
"discriminatorValue"
|
|
62
62
|
)
|
|
63
63
|
self._created_json_template_representation = False
|
|
64
|
+
self._got_polymorphic_subtypes = False
|
|
64
65
|
self.is_public: bool = self.yaml_data.get("isPublic", True)
|
|
65
66
|
self.snake_case_name: str = self.yaml_data["snakeCaseName"]
|
|
66
67
|
|
|
@@ -167,9 +168,13 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
167
168
|
)
|
|
168
169
|
|
|
169
170
|
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
171
|
+
|
|
170
172
|
is_polymorphic_subtype = (
|
|
171
173
|
self.discriminator_value and not self.discriminated_subtypes
|
|
172
174
|
)
|
|
175
|
+
if self._got_polymorphic_subtypes:
|
|
176
|
+
return
|
|
177
|
+
self._got_polymorphic_subtypes = True
|
|
173
178
|
if (
|
|
174
179
|
self.name not in (m.name for m in polymorphic_subtypes)
|
|
175
180
|
and is_polymorphic_subtype
|
|
@@ -179,6 +184,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
179
184
|
discriminated_subtype.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
180
185
|
for property in self.properties:
|
|
181
186
|
property.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
187
|
+
self._got_polymorphic_subtypes = False
|
|
182
188
|
|
|
183
189
|
@classmethod
|
|
184
190
|
def from_yaml(
|
|
@@ -306,6 +306,10 @@ class Parameter(_ParameterBase):
|
|
|
306
306
|
def xml_serialization_ctxt(self) -> str:
|
|
307
307
|
return self.type.xml_serialization_ctxt or ""
|
|
308
308
|
|
|
309
|
+
@property
|
|
310
|
+
def is_content_type(self) -> bool:
|
|
311
|
+
return bool(self.rest_api_name) and self.rest_api_name.lower() == "content-type"
|
|
312
|
+
|
|
309
313
|
@property
|
|
310
314
|
def method_location(self) -> ParameterMethodLocation:
|
|
311
315
|
if not self.in_method_signature:
|
|
@@ -314,7 +318,7 @@ class Parameter(_ParameterBase):
|
|
|
314
318
|
return ParameterMethodLocation.POSITIONAL
|
|
315
319
|
if self.constant:
|
|
316
320
|
return ParameterMethodLocation.KWARG
|
|
317
|
-
if self.
|
|
321
|
+
if self.is_content_type:
|
|
318
322
|
if self.in_overload:
|
|
319
323
|
return ParameterMethodLocation.KEYWORD_ONLY
|
|
320
324
|
return ParameterMethodLocation.KWARG
|
|
@@ -110,14 +110,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
110
110
|
from .model_type import ModelType
|
|
111
111
|
|
|
112
112
|
if isinstance(self.type, ModelType):
|
|
113
|
-
|
|
114
|
-
self.type.discriminator_value and not self.type.discriminated_subtypes
|
|
115
|
-
)
|
|
116
|
-
if (
|
|
117
|
-
self.type.name not in (m.name for m in polymorphic_subtypes)
|
|
118
|
-
and is_polymorphic_subtype
|
|
119
|
-
):
|
|
120
|
-
polymorphic_subtypes.append(self.type)
|
|
113
|
+
self.type.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
121
114
|
|
|
122
115
|
@property
|
|
123
116
|
def validation(self) -> Optional[Dict[str, Any]]:
|
|
@@ -95,7 +95,7 @@ class RequestBuilderParameter(Parameter):
|
|
|
95
95
|
) -> None:
|
|
96
96
|
super().__init__(yaml_data, code_model, type)
|
|
97
97
|
# we don't want any default content type behavior in request builder
|
|
98
|
-
if self.
|
|
98
|
+
if self.is_content_type:
|
|
99
99
|
self.client_default_value = None
|
|
100
100
|
if self.grouped_by and self.client_name[0] == "_":
|
|
101
101
|
# we don't want hidden parameters for grouped by in request builders
|
|
@@ -55,6 +55,10 @@ class Response(BaseModel):
|
|
|
55
55
|
self.type = type
|
|
56
56
|
self.nullable = yaml_data.get("nullable")
|
|
57
57
|
|
|
58
|
+
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
59
|
+
if self.type:
|
|
60
|
+
self.type.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
61
|
+
|
|
58
62
|
def get_json_template_representation(self) -> Any:
|
|
59
63
|
if not self.type:
|
|
60
64
|
return None
|
|
@@ -137,6 +141,9 @@ class PagingResponse(Response):
|
|
|
137
141
|
super().__init__(*args, **kwargs)
|
|
138
142
|
self.item_type = self.code_model.lookup_type(id(self.yaml_data["itemType"]))
|
|
139
143
|
|
|
144
|
+
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
145
|
+
return self.item_type.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
146
|
+
|
|
140
147
|
def get_json_template_representation(self) -> Any:
|
|
141
148
|
return self.item_type.get_json_template_representation()
|
|
142
149
|
|
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
-
import re
|
|
7
6
|
from typing import TypeVar, Dict
|
|
8
7
|
|
|
9
|
-
JSON_REGEXP = re.compile(r"^(application|text)/(.+\+)?json$")
|
|
10
|
-
|
|
11
8
|
T = TypeVar("T")
|
|
12
9
|
OrderedSet = Dict[T, None]
|
|
13
10
|
|
|
@@ -531,7 +531,7 @@ class _OperationSerializer(
|
|
|
531
531
|
polymorphic_subtypes: List[ModelType] = []
|
|
532
532
|
if not response.type:
|
|
533
533
|
continue
|
|
534
|
-
response.
|
|
534
|
+
response.get_polymorphic_subtypes(polymorphic_subtypes)
|
|
535
535
|
if polymorphic_subtypes:
|
|
536
536
|
# we just assume one kind of polymorphic body for input
|
|
537
537
|
discriminator_name = cast(
|
|
@@ -691,7 +691,9 @@ class _OperationSerializer(
|
|
|
691
691
|
if (
|
|
692
692
|
not body_param.default_content_type
|
|
693
693
|
and not next(
|
|
694
|
-
p
|
|
694
|
+
p
|
|
695
|
+
for p in builder.parameters
|
|
696
|
+
if p.rest_api_name.lower() == "content-type"
|
|
695
697
|
).optional
|
|
696
698
|
):
|
|
697
699
|
content_types = "'" + "', '".join(body_param.content_types) + "'"
|
|
@@ -6,28 +6,25 @@
|
|
|
6
6
|
# --------------------------------------------------------------------------
|
|
7
7
|
"""The modelerfour reformatter autorest plugin.
|
|
8
8
|
"""
|
|
9
|
-
import re
|
|
10
9
|
import copy
|
|
11
10
|
import logging
|
|
12
11
|
from typing import Callable, Dict, Any, Iterable, List, Optional, Set
|
|
13
12
|
|
|
14
|
-
from .._utils import
|
|
13
|
+
from .._utils import (
|
|
14
|
+
to_snake_case,
|
|
15
|
+
KNOWN_TYPES,
|
|
16
|
+
get_body_type_for_description,
|
|
17
|
+
JSON_REGEXP,
|
|
18
|
+
)
|
|
15
19
|
from .. import YamlUpdatePluginAutorest
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
|
|
18
22
|
ORIGINAL_ID_TO_UPDATED_TYPE: Dict[int, Dict[str, Any]] = {}
|
|
19
23
|
OAUTH_TYPE = "OAuth2"
|
|
20
24
|
KEY_TYPE = "Key"
|
|
21
25
|
|
|
22
26
|
_LOGGER = logging.getLogger(__name__)
|
|
23
27
|
|
|
24
|
-
# used if we want to get a string / binary type etc
|
|
25
|
-
KNOWN_TYPES: Dict[str, Dict[str, Any]] = {
|
|
26
|
-
"string": {"type": "string"},
|
|
27
|
-
"binary": {"type": "binary"},
|
|
28
|
-
"anydict": {"type": "dict", "elementType": {"type": "any"}},
|
|
29
|
-
}
|
|
30
|
-
|
|
31
28
|
|
|
32
29
|
def is_body(yaml_data: Dict[str, Any]) -> bool:
|
|
33
30
|
"""Return true if passed in parameter is a body param"""
|
|
@@ -329,14 +326,6 @@ def get_all_body_types(yaml_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
|
329
326
|
return list(seen_body_types.values())
|
|
330
327
|
|
|
331
328
|
|
|
332
|
-
def get_body_type_for_description(body_parameter: Dict[str, Any]) -> str:
|
|
333
|
-
if body_parameter["type"]["type"] == "binary":
|
|
334
|
-
return "binary"
|
|
335
|
-
if body_parameter["type"]["type"] == "string":
|
|
336
|
-
return "string"
|
|
337
|
-
return "JSON"
|
|
338
|
-
|
|
339
|
-
|
|
340
329
|
def add_lro_information(operation: Dict[str, Any], yaml_data: Dict[str, Any]) -> None:
|
|
341
330
|
operation["discriminator"] = "lro"
|
|
342
331
|
extensions = yaml_data["extensions"]
|
|
@@ -479,7 +468,7 @@ class M4Reformatter(
|
|
|
479
468
|
group_name, yaml_data, body_type, content_types=content_types
|
|
480
469
|
)
|
|
481
470
|
for parameter in overload["parameters"]:
|
|
482
|
-
if parameter["restApiName"] == "
|
|
471
|
+
if parameter["restApiName"].lower() == "content-type":
|
|
483
472
|
parameter["clientDefaultValue"] = overload["bodyParameter"][
|
|
484
473
|
"defaultContentType"
|
|
485
474
|
]
|
|
@@ -557,23 +546,6 @@ class M4Reformatter(
|
|
|
557
546
|
else None
|
|
558
547
|
)
|
|
559
548
|
content_types = None
|
|
560
|
-
if ( # pylint: disable=too-many-boolean-expressions
|
|
561
|
-
body_parameter
|
|
562
|
-
and body_parameter["type"]["type"] != "combined"
|
|
563
|
-
and yaml_data.get("requestMediaTypes")
|
|
564
|
-
and any(
|
|
565
|
-
ct for ct in yaml_data["requestMediaTypes"] if JSON_REGEXP.match(ct)
|
|
566
|
-
)
|
|
567
|
-
and body_parameter["type"]["type"] in ("model", "dict", "list")
|
|
568
|
-
and not body_parameter["type"]["xmlMetadata"]
|
|
569
|
-
and not body_parameter.get("flattened")
|
|
570
|
-
and not body_parameter.get("groupedBy")
|
|
571
|
-
):
|
|
572
|
-
combined_type = update_types(
|
|
573
|
-
[body_parameter["type"], KNOWN_TYPES["binary"]]
|
|
574
|
-
)
|
|
575
|
-
body_parameter["type"] = combined_type
|
|
576
|
-
content_types = body_parameter["contentTypes"]
|
|
577
549
|
operation = self._update_operation_helper(group_name, yaml_data, body_parameter)
|
|
578
550
|
operation["overloads"] = self.update_overloads(
|
|
579
551
|
group_name, yaml_data, body_parameter, content_types=content_types
|
|
@@ -843,7 +815,10 @@ class M4Reformatter(
|
|
|
843
815
|
continue
|
|
844
816
|
if is_body(param):
|
|
845
817
|
continue
|
|
846
|
-
if
|
|
818
|
+
if (
|
|
819
|
+
param["language"]["default"].get("serializedName").lower()
|
|
820
|
+
== "content-type"
|
|
821
|
+
):
|
|
847
822
|
param = self._update_content_type_parameter(
|
|
848
823
|
param,
|
|
849
824
|
body_parameter,
|
|
@@ -5,13 +5,107 @@
|
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
"""The preprocessing autorest plugin.
|
|
7
7
|
"""
|
|
8
|
+
import copy
|
|
8
9
|
from typing import Callable, Dict, Any, List, Optional
|
|
10
|
+
|
|
9
11
|
from .._utils import to_snake_case
|
|
10
12
|
from .helpers import pad_reserved_words, add_redefined_builtin_info
|
|
11
13
|
from .python_mappings import PadType
|
|
12
14
|
|
|
13
15
|
from .. import YamlUpdatePlugin, YamlUpdatePluginAutorest
|
|
14
|
-
from .._utils import parse_args
|
|
16
|
+
from .._utils import parse_args, get_body_type_for_description, JSON_REGEXP, KNOWN_TYPES
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_body_param_type(code_model: Dict[str, Any], body_parameter: Dict[str, Any]):
|
|
20
|
+
if (
|
|
21
|
+
body_parameter
|
|
22
|
+
and body_parameter["type"]["type"] in ("model", "dict", "list")
|
|
23
|
+
and any(
|
|
24
|
+
ct for ct in body_parameter.get("contentTypes", []) if JSON_REGEXP.match(ct)
|
|
25
|
+
)
|
|
26
|
+
and not body_parameter["type"].get("xmlMetadata")
|
|
27
|
+
and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
|
|
28
|
+
):
|
|
29
|
+
body_parameter["type"] = {
|
|
30
|
+
"type": "combined",
|
|
31
|
+
"types": [body_parameter["type"], KNOWN_TYPES["binary"]],
|
|
32
|
+
}
|
|
33
|
+
code_model["types"].append(body_parameter["type"])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def update_overload_section(
|
|
37
|
+
overload: Dict[str, Any],
|
|
38
|
+
yaml_data: Dict[str, Any],
|
|
39
|
+
section: str,
|
|
40
|
+
):
|
|
41
|
+
for overload_s, original_s in zip(overload[section], yaml_data[section]):
|
|
42
|
+
if overload_s.get("type"):
|
|
43
|
+
overload_s["type"] = original_s["type"]
|
|
44
|
+
if overload_s.get("headers"):
|
|
45
|
+
for overload_h, original_h in zip(
|
|
46
|
+
overload_s["headers"], original_s["headers"]
|
|
47
|
+
):
|
|
48
|
+
if overload_h.get("type"):
|
|
49
|
+
overload_h["type"] = original_h["type"]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def add_overload(yaml_data: Dict[str, Any], body_type: Dict[str, Any]):
|
|
53
|
+
overload = copy.deepcopy(yaml_data)
|
|
54
|
+
overload["isOverload"] = True
|
|
55
|
+
overload["bodyParameter"]["type"] = body_type
|
|
56
|
+
|
|
57
|
+
overload["overloads"] = []
|
|
58
|
+
|
|
59
|
+
# for yaml sync, we need to make sure all of the responses, parameters, and exceptions' types have the same yaml id
|
|
60
|
+
for overload_p, original_p in zip(overload["parameters"], yaml_data["parameters"]):
|
|
61
|
+
overload_p["type"] = original_p["type"]
|
|
62
|
+
update_overload_section(overload, yaml_data, "responses")
|
|
63
|
+
update_overload_section(overload, yaml_data, "exceptions")
|
|
64
|
+
|
|
65
|
+
# update content type to be an overloads content type
|
|
66
|
+
content_type_param = next(
|
|
67
|
+
p for p in overload["parameters"] if p["restApiName"].lower() == "content-type"
|
|
68
|
+
)
|
|
69
|
+
content_type_param["inOverload"] = True
|
|
70
|
+
content_type_param["inDocstring"] = True
|
|
71
|
+
body_type_description = get_body_type_for_description(overload["bodyParameter"])
|
|
72
|
+
content_type_param[
|
|
73
|
+
"description"
|
|
74
|
+
] = f"Body Parameter content-type. Content type parameter for {body_type_description} body."
|
|
75
|
+
content_types = yaml_data["bodyParameter"]["contentTypes"]
|
|
76
|
+
if body_type["type"] == "binary" and len(content_types) > 1:
|
|
77
|
+
content_types = "'" + "', '".join(content_types) + "'"
|
|
78
|
+
content_type_param["description"] += f" Known values are: {content_types}."
|
|
79
|
+
return overload
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def add_overloads_for_body_param(yaml_data: Dict[str, Any]) -> None:
|
|
83
|
+
"""If we added a body parameter type, add overloads for that type"""
|
|
84
|
+
body_parameter = yaml_data["bodyParameter"]
|
|
85
|
+
if not (
|
|
86
|
+
body_parameter["type"]["type"] == "combined"
|
|
87
|
+
and len(yaml_data["bodyParameter"]["type"]["types"])
|
|
88
|
+
> len(yaml_data["overloads"])
|
|
89
|
+
):
|
|
90
|
+
return
|
|
91
|
+
for body_type in body_parameter["type"]["types"]:
|
|
92
|
+
if any(
|
|
93
|
+
o
|
|
94
|
+
for o in yaml_data["overloads"]
|
|
95
|
+
if id(o["bodyParameter"]["type"]) == id(body_type)
|
|
96
|
+
):
|
|
97
|
+
continue
|
|
98
|
+
yaml_data["overloads"].append(add_overload(yaml_data, body_type))
|
|
99
|
+
content_type_param = next(
|
|
100
|
+
p for p in yaml_data["parameters"] if p["restApiName"].lower() == "content-type"
|
|
101
|
+
)
|
|
102
|
+
content_type_param["inOverload"] = False
|
|
103
|
+
content_type_param["inOverriden"] = True
|
|
104
|
+
content_type_param["inDocstring"] = True
|
|
105
|
+
content_type_param[
|
|
106
|
+
"clientDefaultValue"
|
|
107
|
+
] = None # make it none bc it will be overriden, we depend on default of overloads
|
|
108
|
+
content_type_param["optional"] = True
|
|
15
109
|
|
|
16
110
|
|
|
17
111
|
def _remove_paging_maxpagesize(yaml_data: Dict[str, Any]) -> None:
|
|
@@ -103,7 +197,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
103
197
|
|
|
104
198
|
def get_operation_updater(
|
|
105
199
|
self, yaml_data: Dict[str, Any]
|
|
106
|
-
) -> Callable[[Dict[str, Any]], None]:
|
|
200
|
+
) -> Callable[[Dict[str, Any], Dict[str, Any]], None]:
|
|
107
201
|
if yaml_data["discriminator"] == "lropaging":
|
|
108
202
|
return self.update_lro_paging_operation
|
|
109
203
|
if yaml_data["discriminator"] == "lro":
|
|
@@ -112,7 +206,13 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
112
206
|
return self.update_paging_operation
|
|
113
207
|
return self.update_operation
|
|
114
208
|
|
|
115
|
-
def update_operation(
|
|
209
|
+
def update_operation(
|
|
210
|
+
self,
|
|
211
|
+
code_model: Dict[str, Any],
|
|
212
|
+
yaml_data: Dict[str, Any],
|
|
213
|
+
*,
|
|
214
|
+
is_overload: bool = False,
|
|
215
|
+
) -> None:
|
|
116
216
|
yaml_data["groupName"] = pad_reserved_words(
|
|
117
217
|
yaml_data["groupName"], PadType.OPERATION_GROUP
|
|
118
218
|
)
|
|
@@ -123,6 +223,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
123
223
|
yaml_data["description"], yaml_data["name"]
|
|
124
224
|
)
|
|
125
225
|
yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
|
|
226
|
+
body_parameter = yaml_data.get("bodyParameter")
|
|
126
227
|
for parameter in yaml_data["parameters"]:
|
|
127
228
|
update_parameter(parameter)
|
|
128
229
|
if yaml_data.get("bodyParameter"):
|
|
@@ -130,9 +231,13 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
130
231
|
for entry in yaml_data["bodyParameter"].get("entries", []):
|
|
131
232
|
update_parameter(entry)
|
|
132
233
|
for overload in yaml_data.get("overloads", []):
|
|
133
|
-
self.update_operation(overload)
|
|
234
|
+
self.update_operation(code_model, overload, is_overload=True)
|
|
134
235
|
for response in yaml_data.get("responses", []):
|
|
135
236
|
response["discriminator"] = "operation"
|
|
237
|
+
if body_parameter and not is_overload:
|
|
238
|
+
# if we have a JSON body, we add a binary overload
|
|
239
|
+
add_body_param_type(code_model, body_parameter)
|
|
240
|
+
add_overloads_for_body_param(yaml_data)
|
|
136
241
|
|
|
137
242
|
def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
|
|
138
243
|
azure_arm = self.options.get("azure-arm", False)
|
|
@@ -157,23 +262,38 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
157
262
|
else "azure.core.polling.async_base_polling.AsyncLROBasePolling"
|
|
158
263
|
)
|
|
159
264
|
|
|
160
|
-
def update_lro_paging_operation(
|
|
161
|
-
self
|
|
162
|
-
|
|
265
|
+
def update_lro_paging_operation(
|
|
266
|
+
self,
|
|
267
|
+
code_model: Dict[str, Any],
|
|
268
|
+
yaml_data: Dict[str, Any],
|
|
269
|
+
is_overload: bool = False,
|
|
270
|
+
) -> None:
|
|
271
|
+
self.update_lro_operation(code_model, yaml_data, is_overload=is_overload)
|
|
272
|
+
self.update_paging_operation(code_model, yaml_data, is_overload=is_overload)
|
|
163
273
|
yaml_data["discriminator"] = "lropaging"
|
|
164
274
|
for response in yaml_data.get("responses", []):
|
|
165
275
|
response["discriminator"] = "lropaging"
|
|
166
276
|
for overload in yaml_data.get("overloads", []):
|
|
167
|
-
self.update_lro_paging_operation(overload)
|
|
277
|
+
self.update_lro_paging_operation(code_model, overload, is_overload=True)
|
|
168
278
|
|
|
169
|
-
def update_lro_operation(
|
|
170
|
-
self
|
|
279
|
+
def update_lro_operation(
|
|
280
|
+
self,
|
|
281
|
+
code_model: Dict[str, Any],
|
|
282
|
+
yaml_data: Dict[str, Any],
|
|
283
|
+
is_overload: bool = False,
|
|
284
|
+
) -> None:
|
|
285
|
+
self.update_operation(code_model, yaml_data, is_overload=is_overload)
|
|
171
286
|
self._update_lro_operation_helper(yaml_data)
|
|
172
287
|
for overload in yaml_data.get("overloads", []):
|
|
173
288
|
self._update_lro_operation_helper(overload)
|
|
174
289
|
|
|
175
|
-
def update_paging_operation(
|
|
176
|
-
self
|
|
290
|
+
def update_paging_operation(
|
|
291
|
+
self,
|
|
292
|
+
code_model: Dict[str, Any],
|
|
293
|
+
yaml_data: Dict[str, Any],
|
|
294
|
+
is_overload: bool = False,
|
|
295
|
+
) -> None:
|
|
296
|
+
self.update_operation(code_model, yaml_data, is_overload=is_overload)
|
|
177
297
|
if not yaml_data.get("pagerSync"):
|
|
178
298
|
yaml_data["pagerSync"] = "azure.core.paging.ItemPaged"
|
|
179
299
|
if not yaml_data.get("pagerAsync"):
|
|
@@ -208,7 +328,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
208
328
|
update_paging_response(response)
|
|
209
329
|
response["itemType"] = item_type
|
|
210
330
|
for overload in yaml_data.get("overloads", []):
|
|
211
|
-
self.update_paging_operation(overload)
|
|
331
|
+
self.update_paging_operation(code_model, overload, is_overload=True)
|
|
212
332
|
|
|
213
333
|
def update_operation_groups(self, yaml_data: Dict[str, Any]) -> None:
|
|
214
334
|
operation_groups_yaml_data = yaml_data["operationGroups"]
|
|
@@ -223,13 +343,13 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
|
|
|
223
343
|
yaml_data, operation_group["className"]
|
|
224
344
|
)
|
|
225
345
|
for operation in operation_group["operations"]:
|
|
226
|
-
self.get_operation_updater(operation)(operation)
|
|
346
|
+
self.get_operation_updater(operation)(yaml_data, operation)
|
|
227
347
|
|
|
228
348
|
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
|
|
229
349
|
"""Convert in place the YAML str."""
|
|
230
350
|
update_client(yaml_data["client"])
|
|
231
|
-
update_types(yaml_data["types"])
|
|
232
351
|
self.update_operation_groups(yaml_data)
|
|
352
|
+
update_types(yaml_data["types"])
|
|
233
353
|
|
|
234
354
|
|
|
235
355
|
class PreProcessPluginAutorest(YamlUpdatePluginAutorest, PreProcessPlugin):
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autorest/python",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.3",
|
|
4
4
|
"description": "The Python extension for generators in AutoRest.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prepare": "node run-python3.js prepare.py",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"start.py",
|
|
39
39
|
"venvtools.py",
|
|
40
40
|
"run-python3.js",
|
|
41
|
-
"requirements.txt"
|
|
41
|
+
"requirements.txt",
|
|
42
|
+
"run_cadl.py"
|
|
42
43
|
]
|
|
43
44
|
}
|
package/run_cadl.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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 venv
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from venvtools import python_run
|
|
10
|
+
|
|
11
|
+
_ROOT_DIR = Path(__file__).parent
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
venv_path = _ROOT_DIR / "venv"
|
|
15
|
+
venv_prexists = venv_path.exists()
|
|
16
|
+
|
|
17
|
+
assert venv_prexists # Otherwise install was not done
|
|
18
|
+
|
|
19
|
+
env_builder = venv.EnvBuilder(with_pip=True)
|
|
20
|
+
venv_context = env_builder.ensure_directories(venv_path)
|
|
21
|
+
|
|
22
|
+
# run m2r
|
|
23
|
+
python_run(venv_context, "autorest.m2r.__init__", command=sys.argv[1:])
|
|
24
|
+
python_run(venv_context, "autorest.preprocess.__init__", command=sys.argv[1:])
|
|
25
|
+
python_run(venv_context, "autorest.cadlflags.__init__", command=sys.argv[1:])
|
|
26
|
+
python_run(venv_context, "autorest.codegen.__init__", command=sys.argv[1:])
|
|
27
|
+
python_run(venv_context, "autorest.black.__init__", command=sys.argv[1:])
|