@autorest/python 5.12.6 → 5.15.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 +205 -125
- package/autorest/black/__init__.py +3 -0
- package/autorest/codegen/__init__.py +120 -48
- package/autorest/codegen/models/__init__.py +2 -1
- package/autorest/codegen/models/base_schema.py +2 -6
- package/autorest/codegen/models/client.py +6 -0
- package/autorest/codegen/models/code_model.py +43 -74
- package/autorest/codegen/models/constant_schema.py +7 -7
- package/autorest/codegen/models/credential_model.py +47 -0
- package/autorest/codegen/models/credential_schema.py +5 -4
- package/autorest/codegen/models/dictionary_schema.py +7 -7
- package/autorest/codegen/models/enum_schema.py +8 -39
- package/autorest/codegen/models/imports.py +3 -1
- package/autorest/codegen/models/list_schema.py +18 -8
- package/autorest/codegen/models/lro_operation.py +3 -3
- package/autorest/codegen/models/lro_paging_operation.py +3 -3
- package/autorest/codegen/models/object_schema.py +17 -13
- package/autorest/codegen/models/operation.py +38 -10
- package/autorest/codegen/models/operation_group.py +7 -2
- package/autorest/codegen/models/paging_operation.py +3 -3
- package/autorest/codegen/models/parameter.py +71 -22
- package/autorest/codegen/models/parameter_list.py +11 -5
- package/autorest/codegen/models/primitive_schemas.py +15 -25
- package/autorest/codegen/models/property.py +5 -5
- package/autorest/codegen/models/request_builder.py +4 -4
- package/autorest/codegen/models/request_builder_parameter.py +17 -5
- package/autorest/codegen/models/schema_response.py +23 -10
- package/autorest/codegen/models/utils.py +20 -0
- package/autorest/codegen/serializers/__init__.py +184 -87
- package/autorest/codegen/serializers/builder_serializer.py +113 -47
- package/autorest/codegen/serializers/client_serializer.py +16 -6
- package/autorest/codegen/serializers/general_serializer.py +28 -4
- package/autorest/codegen/serializers/import_serializer.py +1 -1
- package/autorest/codegen/serializers/metadata_serializer.py +1 -1
- package/autorest/codegen/serializers/model_base_serializer.py +8 -0
- package/autorest/codegen/serializers/model_python3_serializer.py +2 -2
- package/autorest/codegen/serializers/operation_groups_serializer.py +1 -0
- package/autorest/codegen/serializers/patch_serializer.py +12 -3
- package/autorest/codegen/serializers/utils.py +29 -4
- package/autorest/codegen/templates/CHANGELOG.md.jinja2 +6 -0
- package/autorest/codegen/templates/LICENSE.jinja2 +21 -0
- package/autorest/codegen/templates/MANIFEST.in.jinja2 +7 -0
- package/autorest/codegen/templates/README.md.jinja2 +105 -0
- package/autorest/codegen/templates/config.py.jinja2 +4 -4
- package/autorest/codegen/templates/dev_requirements.txt.jinja2 +10 -0
- package/autorest/codegen/templates/enum.py.jinja2 +1 -1
- package/autorest/codegen/templates/enum_container.py.jinja2 +0 -1
- package/autorest/codegen/templates/init.py.jinja2 +9 -6
- package/autorest/codegen/templates/keywords.jinja2 +14 -1
- package/autorest/codegen/templates/lro_operation.py.jinja2 +1 -1
- package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +1 -1
- package/autorest/codegen/templates/metadata.json.jinja2 +3 -3
- package/autorest/codegen/templates/model.py.jinja2 +1 -6
- package/autorest/codegen/templates/model_init.py.jinja2 +7 -4
- package/autorest/codegen/templates/operation.py.jinja2 +2 -3
- package/autorest/codegen/templates/operation_group.py.jinja2 +20 -17
- package/autorest/codegen/templates/operation_groups_container.py.jinja2 +0 -1
- package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
- package/autorest/codegen/templates/paging_operation.py.jinja2 +1 -1
- package/autorest/codegen/templates/patch.py.jinja2 +18 -29
- package/autorest/codegen/templates/request_builder.py.jinja2 +4 -6
- package/autorest/codegen/templates/setup.py.jinja2 +79 -20
- package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
- package/autorest/multiapi/models/imports.py +21 -11
- package/autorest/multiapi/serializers/import_serializer.py +3 -1
- package/autorest/namer/name_converter.py +1 -1
- package/package.json +2 -2
- package/run-python3.js +1 -7
- package/venvtools.py +2 -2
|
@@ -59,13 +59,11 @@ class EnumSchema(BaseSchema):
|
|
|
59
59
|
name: str,
|
|
60
60
|
values: List["EnumValue"],
|
|
61
61
|
enum_type: PrimitiveSchema,
|
|
62
|
-
enum_file_name: str
|
|
63
62
|
) -> None:
|
|
64
63
|
super(EnumSchema, self).__init__(namespace=namespace, yaml_data=yaml_data)
|
|
65
64
|
self.description = description
|
|
66
65
|
self.name = name
|
|
67
66
|
self.values = values
|
|
68
|
-
self.enum_file_name = enum_file_name
|
|
69
67
|
self.enum_type = enum_type
|
|
70
68
|
|
|
71
69
|
def __lt__(self, other):
|
|
@@ -80,23 +78,13 @@ class EnumSchema(BaseSchema):
|
|
|
80
78
|
"""
|
|
81
79
|
return self.enum_type.serialization_type
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
def type_annotation(self) -> str:
|
|
81
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str:
|
|
85
82
|
"""The python type used for type annotation
|
|
86
83
|
|
|
87
84
|
:return: The type annotation for this schema
|
|
88
85
|
:rtype: str
|
|
89
86
|
"""
|
|
90
|
-
return f'Union[{self.enum_type.type_annotation}, "{self.name}"]'
|
|
91
|
-
|
|
92
|
-
@property
|
|
93
|
-
def operation_type_annotation(self) -> str:
|
|
94
|
-
"""The python type used for type annotation
|
|
95
|
-
|
|
96
|
-
:return: The type annotation for this schema
|
|
97
|
-
:rtype: str
|
|
98
|
-
"""
|
|
99
|
-
return f'Union[{self.enum_type.type_annotation}, "_models.{self.name}"]'
|
|
87
|
+
return f'Union[{self.enum_type.type_annotation(is_operation_file=is_operation_file)}, "_models.{self.name}"]'
|
|
100
88
|
|
|
101
89
|
def get_declaration(self, value: Any) -> str:
|
|
102
90
|
return self.enum_type.get_declaration(value)
|
|
@@ -109,7 +97,7 @@ class EnumSchema(BaseSchema):
|
|
|
109
97
|
def docstring_type(self) -> str:
|
|
110
98
|
"""The python type used for RST syntax input and type annotation.
|
|
111
99
|
"""
|
|
112
|
-
return f"{self.enum_type.type_annotation} or ~{self.namespace}.models.{self.name}"
|
|
100
|
+
return f"{self.enum_type.type_annotation()} or ~{self.namespace}.models.{self.name}"
|
|
113
101
|
|
|
114
102
|
@staticmethod
|
|
115
103
|
def _get_enum_values(yaml_data: List[Dict[str, Any]]) -> List["EnumValue"]:
|
|
@@ -164,8 +152,6 @@ class EnumSchema(BaseSchema):
|
|
|
164
152
|
else:
|
|
165
153
|
enum_type = StringSchema(namespace, {"type": "str"})
|
|
166
154
|
values = EnumSchema._get_enum_values(yaml_data["choices"])
|
|
167
|
-
code_model = kwargs.pop("code_model")
|
|
168
|
-
|
|
169
155
|
return cls(
|
|
170
156
|
namespace=namespace,
|
|
171
157
|
yaml_data=yaml_data,
|
|
@@ -173,7 +159,6 @@ class EnumSchema(BaseSchema):
|
|
|
173
159
|
name=name,
|
|
174
160
|
values=values,
|
|
175
161
|
enum_type=enum_type,
|
|
176
|
-
enum_file_name=f"_{code_model.module_name}_enums"
|
|
177
162
|
)
|
|
178
163
|
|
|
179
164
|
def imports(self) -> FileImport:
|
|
@@ -182,12 +167,6 @@ class EnumSchema(BaseSchema):
|
|
|
182
167
|
file_import.merge(self.enum_type.imports())
|
|
183
168
|
return file_import
|
|
184
169
|
|
|
185
|
-
def model_file_imports(self) -> FileImport:
|
|
186
|
-
imports = self.imports()
|
|
187
|
-
# we import every enum since we can get extremely long imports
|
|
188
|
-
# if we import my name
|
|
189
|
-
imports.add_submodule_import("." + self.enum_file_name, "*", ImportType.LOCAL)
|
|
190
|
-
return imports
|
|
191
170
|
|
|
192
171
|
class HiddenModelEnumSchema(EnumSchema):
|
|
193
172
|
|
|
@@ -196,27 +175,17 @@ class HiddenModelEnumSchema(EnumSchema):
|
|
|
196
175
|
file_import.merge(self.enum_type.imports())
|
|
197
176
|
return file_import
|
|
198
177
|
|
|
199
|
-
|
|
200
|
-
def type_annotation(self) -> str:
|
|
201
|
-
"""The python type used for type annotation
|
|
202
|
-
|
|
203
|
-
:return: The type annotation for this schema
|
|
204
|
-
:rtype: str
|
|
205
|
-
"""
|
|
206
|
-
return self.enum_type.type_annotation
|
|
207
|
-
|
|
208
|
-
@property
|
|
209
|
-
def operation_type_annotation(self) -> str:
|
|
178
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str:
|
|
210
179
|
"""The python type used for type annotation
|
|
211
180
|
|
|
212
181
|
:return: The type annotation for this schema
|
|
213
182
|
:rtype: str
|
|
214
183
|
"""
|
|
215
|
-
return self.enum_type.type_annotation
|
|
184
|
+
return self.enum_type.type_annotation(is_operation_file=is_operation_file)
|
|
216
185
|
|
|
217
186
|
@property
|
|
218
187
|
def docstring_text(self) -> str:
|
|
219
|
-
return f"{self.enum_type.type_annotation}. {self.extra_description_information}"
|
|
188
|
+
return f"{self.enum_type.type_annotation()}. {self.extra_description_information}"
|
|
220
189
|
|
|
221
190
|
@property
|
|
222
191
|
def extra_description_information(self):
|
|
@@ -232,13 +201,13 @@ class HiddenModelEnumSchema(EnumSchema):
|
|
|
232
201
|
possible_values[: len(possible_values) - 1]
|
|
233
202
|
) + f", and {possible_values[-1]}"
|
|
234
203
|
|
|
235
|
-
return "
|
|
204
|
+
return "Known values are: {}.".format(possible_values_str)
|
|
236
205
|
|
|
237
206
|
@property
|
|
238
207
|
def docstring_type(self) -> str:
|
|
239
208
|
"""The python type used for RST syntax input and type annotation.
|
|
240
209
|
"""
|
|
241
|
-
return self.enum_type.type_annotation
|
|
210
|
+
return self.enum_type.type_annotation()
|
|
242
211
|
|
|
243
212
|
def get_enum_schema(code_model) -> Type[EnumSchema]:
|
|
244
213
|
if code_model.options["models_mode"]:
|
|
@@ -97,13 +97,15 @@ class FileImport:
|
|
|
97
97
|
self,
|
|
98
98
|
module_name: str,
|
|
99
99
|
import_type: ImportType,
|
|
100
|
-
typing_section: TypingSection = TypingSection.REGULAR
|
|
100
|
+
typing_section: TypingSection = TypingSection.REGULAR,
|
|
101
|
+
alias: Optional[str] = None,
|
|
101
102
|
) -> None:
|
|
102
103
|
# Implementation detail: a regular import is just a "from" with no from
|
|
103
104
|
self._append_import(ImportModel(
|
|
104
105
|
typing_section=typing_section,
|
|
105
106
|
import_type=import_type,
|
|
106
107
|
module_name=module_name,
|
|
108
|
+
alias=alias,
|
|
107
109
|
))
|
|
108
110
|
|
|
109
111
|
def define_mypy_type(self, type_name: str, type_value: str, async_type_value: Optional[str] = None):
|
|
@@ -29,20 +29,24 @@ class ListSchema(BaseSchema):
|
|
|
29
29
|
def serialization_type(self) -> str:
|
|
30
30
|
return f"[{self.element_type.serialization_type}]"
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def operation_type_annotation(self) -> str:
|
|
38
|
-
return f"List[{self.element_type.operation_type_annotation}]"
|
|
32
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str:
|
|
33
|
+
if self.element_type.type_annotation() == "ET.Element":
|
|
34
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
35
|
+
return self.element_type.type_annotation(is_operation_file=is_operation_file)
|
|
36
|
+
return f"List[{self.element_type.type_annotation(is_operation_file=is_operation_file)}]"
|
|
39
37
|
|
|
40
38
|
@property
|
|
41
39
|
def docstring_type(self) -> str:
|
|
40
|
+
if self.element_type.docstring_type == "ET.Element":
|
|
41
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
42
|
+
return self.element_type.docstring_type
|
|
42
43
|
return f"list[{self.element_type.docstring_type}]"
|
|
43
44
|
|
|
44
45
|
@property
|
|
45
46
|
def docstring_text(self) -> str:
|
|
47
|
+
if self.element_type.docstring_text == "XML Element":
|
|
48
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
49
|
+
return self.element_type.docstring_text
|
|
46
50
|
return f"list of {self.element_type.docstring_text}"
|
|
47
51
|
|
|
48
52
|
@property
|
|
@@ -108,6 +112,12 @@ class ListSchema(BaseSchema):
|
|
|
108
112
|
|
|
109
113
|
def imports(self) -> FileImport:
|
|
110
114
|
file_import = FileImport()
|
|
111
|
-
|
|
115
|
+
if not self.element_type.type_annotation(is_operation_file=True) == "ET.Element":
|
|
116
|
+
file_import.add_submodule_import("typing", "List", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
112
117
|
file_import.merge(self.element_type.imports())
|
|
113
118
|
return file_import
|
|
119
|
+
|
|
120
|
+
def model_file_imports(self) -> FileImport:
|
|
121
|
+
file_import = self.imports()
|
|
122
|
+
file_import.merge(self.element_type.model_file_imports())
|
|
123
|
+
return file_import
|
|
@@ -143,8 +143,8 @@ class LROOperation(Operation):
|
|
|
143
143
|
file_import.add_submodule_import(poller_import_path, poller, ImportType.AZURECORE, TypingSection.CONDITIONAL)
|
|
144
144
|
return file_import
|
|
145
145
|
|
|
146
|
-
def imports(self, async_mode: bool) -> FileImport:
|
|
147
|
-
file_import =
|
|
146
|
+
def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
147
|
+
file_import = self._imports_base(async_mode, is_python3_file)
|
|
148
148
|
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
149
149
|
|
|
150
150
|
poller_import_path = ".".join(self.get_poller_path(async_mode).split(".")[:-1])
|
|
@@ -172,7 +172,7 @@ class LROOperation(Operation):
|
|
|
172
172
|
)
|
|
173
173
|
base_polling_method = self.get_base_polling_method(async_mode)
|
|
174
174
|
file_import.add_submodule_import(base_polling_method_import_path, base_polling_method, ImportType.AZURECORE)
|
|
175
|
-
|
|
175
|
+
file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
|
|
176
176
|
if async_mode:
|
|
177
177
|
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
178
178
|
if self.code_model.options["tracing"] and self.want_tracing:
|
|
@@ -9,9 +9,9 @@ from .paging_operation import PagingOperation
|
|
|
9
9
|
|
|
10
10
|
class LROPagingOperation(PagingOperation, LROOperation):
|
|
11
11
|
|
|
12
|
-
def imports(self, async_mode: bool) -> FileImport:
|
|
13
|
-
lro_imports = LROOperation.imports(self, async_mode)
|
|
14
|
-
paging_imports = PagingOperation.imports(self, async_mode)
|
|
12
|
+
def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
13
|
+
lro_imports = LROOperation.imports(self, async_mode, is_python3_file)
|
|
14
|
+
paging_imports = PagingOperation.imports(self, async_mode, is_python3_file)
|
|
15
15
|
|
|
16
16
|
file_import = lro_imports
|
|
17
17
|
file_import.merge(paging_imports)
|
|
@@ -39,13 +39,9 @@ class ObjectSchema(BaseSchema): # pylint: disable=too-many-instance-attributes
|
|
|
39
39
|
def serialization_type(self) -> str:
|
|
40
40
|
return self.name
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return f'"{
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def operation_type_annotation(self) -> str:
|
|
48
|
-
return f'"_models.{self.name}"'
|
|
42
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str:
|
|
43
|
+
retval = f"_models.{self.name}"
|
|
44
|
+
return retval if is_operation_file else f'"{retval}"'
|
|
49
45
|
|
|
50
46
|
@property
|
|
51
47
|
def docstring_type(self) -> str:
|
|
@@ -216,31 +212,39 @@ class ObjectSchema(BaseSchema): # pylint: disable=too-many-instance-attributes
|
|
|
216
212
|
file_import.add_submodule_import("azure.core.exceptions", "HttpResponseError", ImportType.AZURECORE)
|
|
217
213
|
return file_import
|
|
218
214
|
|
|
215
|
+
def model_file_imports(self) -> FileImport:
|
|
216
|
+
file_import = self.imports()
|
|
217
|
+
file_import.add_import("__init__", ImportType.LOCAL, typing_section=TypingSection.TYPING, alias="_models")
|
|
218
|
+
return file_import
|
|
219
|
+
|
|
219
220
|
class HiddenModelObjectSchema(ObjectSchema):
|
|
220
221
|
|
|
221
222
|
@property
|
|
222
223
|
def serialization_type(self) -> str:
|
|
223
224
|
return "object"
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@property
|
|
230
|
-
def operation_type_annotation(self) -> str:
|
|
226
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
|
|
227
|
+
if self.xml_metadata:
|
|
228
|
+
return "ET.Element"
|
|
231
229
|
return "JSONType"
|
|
232
230
|
|
|
233
231
|
@property
|
|
234
232
|
def docstring_type(self) -> str:
|
|
233
|
+
if self.xml_metadata:
|
|
234
|
+
return "ET.Element"
|
|
235
235
|
return "JSONType"
|
|
236
236
|
|
|
237
237
|
@property
|
|
238
238
|
def docstring_text(self) -> str:
|
|
239
|
+
if self.xml_metadata:
|
|
240
|
+
return "XML Element"
|
|
239
241
|
return "JSON object"
|
|
240
242
|
|
|
241
243
|
def imports(self) -> FileImport:
|
|
242
244
|
file_import = FileImport()
|
|
243
245
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
246
|
+
if self.xml_metadata:
|
|
247
|
+
file_import.add_submodule_import("xml.etree", "ElementTree", ImportType.STDLIB, alias="ET")
|
|
244
248
|
return file_import
|
|
245
249
|
|
|
246
250
|
def get_object_schema(code_model) -> Type[ObjectSchema]:
|
|
@@ -10,7 +10,7 @@ from typing import cast, Dict, List, Any, Optional, Union, Set
|
|
|
10
10
|
from .base_builder import BaseBuilder, create_parameters
|
|
11
11
|
from .imports import FileImport, ImportType, TypingSection
|
|
12
12
|
from .schema_response import SchemaResponse
|
|
13
|
-
from .parameter import Parameter, get_parameter
|
|
13
|
+
from .parameter import Parameter, get_parameter, ParameterLocation
|
|
14
14
|
from .parameter_list import ParameterList, get_parameter_list
|
|
15
15
|
from .base_schema import BaseSchema
|
|
16
16
|
from .object_schema import ObjectSchema
|
|
@@ -159,7 +159,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
159
159
|
if response.has_body:
|
|
160
160
|
file_import.merge(cast(BaseSchema, response.schema).imports())
|
|
161
161
|
|
|
162
|
-
response_types = [r.
|
|
162
|
+
response_types = [r.type_annotation(is_operation_file=True) for r in self.responses if r.has_body]
|
|
163
163
|
if len(set(response_types)) > 1:
|
|
164
164
|
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
165
165
|
|
|
@@ -171,7 +171,17 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
171
171
|
def imports_for_multiapi(self, async_mode: bool) -> FileImport: # pylint: disable=unused-argument
|
|
172
172
|
return self._imports_shared(async_mode)
|
|
173
173
|
|
|
174
|
-
def imports(self, async_mode: bool) -> FileImport:
|
|
174
|
+
def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
175
|
+
file_import = self._imports_base(async_mode, is_python3_file)
|
|
176
|
+
if self.has_response_body and not self.has_optional_return_type and not self.code_model.options["models_mode"]:
|
|
177
|
+
file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
|
|
178
|
+
return file_import
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def has_kwargs_to_pop_with_default(kwargs_to_pop: List[Parameter], location: ParameterLocation) -> bool:
|
|
182
|
+
return any(kwarg.has_default_value and kwarg.location == location for kwarg in kwargs_to_pop)
|
|
183
|
+
|
|
184
|
+
def _imports_base(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
175
185
|
file_import = self._imports_shared(async_mode)
|
|
176
186
|
|
|
177
187
|
# Exceptions
|
|
@@ -186,6 +196,10 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
186
196
|
file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
187
197
|
file_import.add_submodule_import("azure.core.pipeline", "PipelineResponse", ImportType.AZURECORE)
|
|
188
198
|
file_import.add_submodule_import("azure.core.rest", "HttpRequest", ImportType.AZURECORE)
|
|
199
|
+
kwargs_to_pop = self.parameters.kwargs_to_pop(is_python3_file)
|
|
200
|
+
if (self.has_kwargs_to_pop_with_default(kwargs_to_pop, ParameterLocation.Header) or
|
|
201
|
+
self.has_kwargs_to_pop_with_default(kwargs_to_pop, ParameterLocation.Query)):
|
|
202
|
+
file_import.add_submodule_import("azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE)
|
|
189
203
|
if async_mode:
|
|
190
204
|
file_import.add_submodule_import("azure.core.pipeline.transport", "AsyncHttpResponse", ImportType.AZURECORE)
|
|
191
205
|
else:
|
|
@@ -218,6 +232,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
218
232
|
file_import.add_submodule_import(
|
|
219
233
|
f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
|
|
220
234
|
)
|
|
235
|
+
|
|
221
236
|
if self.code_model.options["version_tolerant"] and (
|
|
222
237
|
self.parameters.has_body or
|
|
223
238
|
any(r for r in self.responses if r.has_body)
|
|
@@ -267,7 +282,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
267
282
|
|
|
268
283
|
def convert_multiple_content_type_parameters(self) -> None:
|
|
269
284
|
type_annot = ", ".join([
|
|
270
|
-
param.schema.
|
|
285
|
+
param.schema.type_annotation(is_operation_file=True)
|
|
271
286
|
for param in self.multiple_content_type_parameters
|
|
272
287
|
])
|
|
273
288
|
docstring_type = " or ".join([
|
|
@@ -303,6 +318,15 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
303
318
|
parameters, multiple_content_type_parameters = create_parameters(
|
|
304
319
|
yaml_data, code_model, parameter_creator
|
|
305
320
|
)
|
|
321
|
+
parameter_list = parameter_list_creator(code_model, parameters, schema_requests)
|
|
322
|
+
multiple_content_type_parameter_list = parameter_list_creator(
|
|
323
|
+
code_model, multiple_content_type_parameters, schema_requests
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if len(parameter_list.content_types) > 1:
|
|
327
|
+
for p in parameter_list.parameters:
|
|
328
|
+
if p.rest_api_name == "Content-Type":
|
|
329
|
+
p.is_keyword_only = True
|
|
306
330
|
|
|
307
331
|
return cls(
|
|
308
332
|
code_model=code_model,
|
|
@@ -310,13 +334,17 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
|
|
|
310
334
|
name=name,
|
|
311
335
|
description=yaml_data["language"]["python"]["description"],
|
|
312
336
|
api_versions=set(value_dict["version"] for value_dict in yaml_data["apiVersions"]),
|
|
313
|
-
parameters=
|
|
314
|
-
multiple_content_type_parameters=
|
|
315
|
-
code_model, multiple_content_type_parameters, schema_requests
|
|
316
|
-
),
|
|
337
|
+
parameters=parameter_list,
|
|
338
|
+
multiple_content_type_parameters=multiple_content_type_parameter_list,
|
|
317
339
|
schema_requests=schema_requests,
|
|
318
340
|
summary=yaml_data["language"]["python"].get("summary"),
|
|
319
|
-
responses=[
|
|
341
|
+
responses=[
|
|
342
|
+
SchemaResponse.from_yaml(yaml, code_model=code_model)
|
|
343
|
+
for yaml in yaml_data.get("responses", [])
|
|
344
|
+
],
|
|
320
345
|
# Exception with no schema means default exception, we don't store them
|
|
321
|
-
exceptions=[
|
|
346
|
+
exceptions=[
|
|
347
|
+
SchemaResponse.from_yaml(yaml, code_model=code_model)
|
|
348
|
+
for yaml in yaml_data.get("exceptions", []) if "schema" in yaml
|
|
349
|
+
],
|
|
322
350
|
)
|
|
@@ -54,15 +54,16 @@ class OperationGroup(BaseModel):
|
|
|
54
54
|
file_import = FileImport()
|
|
55
55
|
for operation in self.operations:
|
|
56
56
|
file_import.merge(operation.imports_for_multiapi(async_mode))
|
|
57
|
+
file_import.add_submodule_import(".." if async_mode else ".", "models", ImportType.LOCAL, alias="_models")
|
|
57
58
|
return file_import
|
|
58
59
|
|
|
59
|
-
def imports(self, async_mode: bool) -> FileImport:
|
|
60
|
+
def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
60
61
|
file_import = FileImport()
|
|
61
62
|
file_import.add_submodule_import("azure.core.exceptions", "ClientAuthenticationError", ImportType.AZURECORE)
|
|
62
63
|
file_import.add_submodule_import("azure.core.exceptions", "ResourceNotFoundError", ImportType.AZURECORE)
|
|
63
64
|
file_import.add_submodule_import("azure.core.exceptions", "ResourceExistsError", ImportType.AZURECORE)
|
|
64
65
|
for operation in self.operations:
|
|
65
|
-
file_import.merge(operation.imports(async_mode))
|
|
66
|
+
file_import.merge(operation.imports(async_mode, is_python3_file))
|
|
66
67
|
local_path = "..." if async_mode else ".."
|
|
67
68
|
if self.code_model.has_schemas and self.code_model.options["models_mode"]:
|
|
68
69
|
file_import.add_submodule_import(local_path, "models", ImportType.LOCAL, alias="_models")
|
|
@@ -84,6 +85,10 @@ class OperationGroup(BaseModel):
|
|
|
84
85
|
request_builder.name,
|
|
85
86
|
import_type=ImportType.LOCAL
|
|
86
87
|
)
|
|
88
|
+
if self.code_model.need_mixin_abc:
|
|
89
|
+
file_import.add_submodule_import(
|
|
90
|
+
".._vendor", "MixinABC", ImportType.LOCAL
|
|
91
|
+
)
|
|
87
92
|
type_value = "Optional[Callable[[PipelineResponse[HttpRequest, {}HttpResponse], T, Dict[str, Any]], Any]]"
|
|
88
93
|
file_import.define_mypy_type(
|
|
89
94
|
"ClsType",
|
|
@@ -66,7 +66,7 @@ class PagingOperation(Operation):
|
|
|
66
66
|
return response
|
|
67
67
|
|
|
68
68
|
def _find_python_name(self, rest_api_name: str, log_name: str) -> str:
|
|
69
|
-
response = self.
|
|
69
|
+
response = self.responses[0]
|
|
70
70
|
response_schema = cast(ObjectSchema, response.schema)
|
|
71
71
|
if response_schema:
|
|
72
72
|
for prop in response_schema.properties:
|
|
@@ -145,8 +145,8 @@ class PagingOperation(Operation):
|
|
|
145
145
|
|
|
146
146
|
return file_import
|
|
147
147
|
|
|
148
|
-
def imports(self, async_mode: bool) -> FileImport:
|
|
149
|
-
file_import =
|
|
148
|
+
def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
|
|
149
|
+
file_import = self._imports_base(async_mode, is_python3_file)
|
|
150
150
|
# operation adds an import for distributed_trace_async, we don't want it
|
|
151
151
|
file_import.imports = [i for i in file_import.imports if not i.submodule_name == "distributed_trace_async"]
|
|
152
152
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import logging
|
|
7
7
|
from enum import Enum
|
|
8
8
|
|
|
9
|
-
from typing import Dict, Optional, List, Any, Union, Tuple, cast
|
|
9
|
+
from typing import Dict, Optional, List, Any, Union, Tuple, cast, TYPE_CHECKING
|
|
10
10
|
|
|
11
11
|
from .imports import FileImport, ImportType, TypingSection
|
|
12
12
|
from .base_model import BaseModel
|
|
@@ -15,6 +15,10 @@ from .constant_schema import ConstantSchema
|
|
|
15
15
|
from .object_schema import ObjectSchema
|
|
16
16
|
from .property import Property
|
|
17
17
|
from .primitive_schemas import IOSchema
|
|
18
|
+
from .utils import get_schema
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from .code_model import CodeModel
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -46,6 +50,15 @@ class ParameterStyle(Enum):
|
|
|
46
50
|
multipart = "multipart"
|
|
47
51
|
|
|
48
52
|
|
|
53
|
+
|
|
54
|
+
def get_target_property_name(code_model: "CodeModel", target_property_id: int) -> str:
|
|
55
|
+
for obj in code_model.schemas.values():
|
|
56
|
+
for prop in obj.properties:
|
|
57
|
+
if prop.id == target_property_id:
|
|
58
|
+
return prop.name
|
|
59
|
+
raise KeyError("Didn't find the target property")
|
|
60
|
+
|
|
61
|
+
|
|
49
62
|
class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too-many-public-methods
|
|
50
63
|
def __init__(
|
|
51
64
|
self,
|
|
@@ -68,7 +81,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
68
81
|
grouped_by: Optional["Parameter"] = None,
|
|
69
82
|
original_parameter: Optional["Parameter"] = None,
|
|
70
83
|
client_default_value: Optional[Any] = None,
|
|
71
|
-
keyword_only: bool =
|
|
84
|
+
keyword_only: Optional[bool] = None,
|
|
72
85
|
content_types: Optional[List[str]] = None,
|
|
73
86
|
) -> None:
|
|
74
87
|
super().__init__(yaml_data)
|
|
@@ -99,6 +112,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
99
112
|
self.body_kwargs: List[Parameter] = []
|
|
100
113
|
self.is_body_kwarg = False
|
|
101
114
|
self.need_import = True
|
|
115
|
+
self.is_kwarg = (self.rest_api_name == "Content-Type" or (self.constant and self.inputtable_by_user))
|
|
102
116
|
|
|
103
117
|
def __hash__(self) -> int:
|
|
104
118
|
return hash(self.serialized_name)
|
|
@@ -111,6 +125,14 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
111
125
|
if description:
|
|
112
126
|
description += " "
|
|
113
127
|
description += f"{self.schema.extra_description_information}"
|
|
128
|
+
if isinstance(self.schema, ConstantSchema) and not self.constant:
|
|
129
|
+
if description:
|
|
130
|
+
description += " "
|
|
131
|
+
description += f"Known values are {self.schema.get_declaration(self.schema.value)} or {None}."
|
|
132
|
+
if self.has_default_value and not any(
|
|
133
|
+
l for l in ["default value is", "default is"] if l in description.lower()
|
|
134
|
+
):
|
|
135
|
+
description += f" Default value is {self.default_value_declaration}."
|
|
114
136
|
if self.constant:
|
|
115
137
|
description += " Note that overriding this default value may result in unsupported behavior."
|
|
116
138
|
return description
|
|
@@ -166,6 +188,10 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
166
188
|
def is_body(self) -> bool:
|
|
167
189
|
return self.location == ParameterLocation.Body
|
|
168
190
|
|
|
191
|
+
@property
|
|
192
|
+
def inputtable_by_user(self) -> bool:
|
|
193
|
+
return self.rest_api_name != "Accept"
|
|
194
|
+
|
|
169
195
|
@property
|
|
170
196
|
def pre_semicolon_content_types(self) -> List[str]:
|
|
171
197
|
"""Splits on semicolon of media types and returns the first half.
|
|
@@ -176,8 +202,8 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
176
202
|
@property
|
|
177
203
|
def in_method_signature(self) -> bool:
|
|
178
204
|
return not(
|
|
179
|
-
# don't put
|
|
180
|
-
self.
|
|
205
|
+
# if not inputtable, don't put in signature
|
|
206
|
+
not self.inputtable_by_user
|
|
181
207
|
# If i'm not in the method code, no point in being in signature
|
|
182
208
|
or not self.in_method_code
|
|
183
209
|
# If I'm grouped, my grouper will be on signature, not me
|
|
@@ -216,7 +242,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
216
242
|
) and isinstance(self.schema, IOSchema)
|
|
217
243
|
|
|
218
244
|
def _default_value(self) -> Tuple[Optional[Any], str, str]:
|
|
219
|
-
type_annot = self.multiple_content_types_type_annot or self.schema.
|
|
245
|
+
type_annot = self.multiple_content_types_type_annot or self.schema.type_annotation(is_operation_file=True)
|
|
220
246
|
if self._is_io_json:
|
|
221
247
|
type_annot = f"Union[{type_annot}, JSONType]"
|
|
222
248
|
any_types = ["Any", "JSONType"]
|
|
@@ -232,7 +258,12 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
232
258
|
default_value_declaration = "None"
|
|
233
259
|
else:
|
|
234
260
|
if isinstance(self.schema, ConstantSchema):
|
|
235
|
-
|
|
261
|
+
if (self.required or
|
|
262
|
+
self.is_content_type or
|
|
263
|
+
not self.code_model.options["default_optional_constants_to_none"]):
|
|
264
|
+
default_value = self.schema.get_declaration(self.schema.value)
|
|
265
|
+
else:
|
|
266
|
+
default_value = None
|
|
236
267
|
default_value_declaration = default_value
|
|
237
268
|
else:
|
|
238
269
|
default_value = self.schema.default_value
|
|
@@ -263,8 +294,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
263
294
|
def default_value_declaration(self) -> Optional[Any]:
|
|
264
295
|
return self._default_value()[1]
|
|
265
296
|
|
|
266
|
-
|
|
267
|
-
def type_annotation(self) -> str:
|
|
297
|
+
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
|
|
268
298
|
return self._default_value()[2]
|
|
269
299
|
|
|
270
300
|
@property
|
|
@@ -283,13 +313,14 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
283
313
|
return self.default_value is not None or not self.required
|
|
284
314
|
|
|
285
315
|
def method_signature(self, is_python3_file: bool) -> str:
|
|
316
|
+
type_annot = self.type_annotation(is_operation_file=True)
|
|
286
317
|
if is_python3_file:
|
|
287
318
|
if self.has_default_value:
|
|
288
|
-
return f"{self.serialized_name}: {
|
|
289
|
-
return f"{self.serialized_name}: {
|
|
319
|
+
return f"{self.serialized_name}: {type_annot} = {self.default_value_declaration},"
|
|
320
|
+
return f"{self.serialized_name}: {type_annot},"
|
|
290
321
|
if self.has_default_value:
|
|
291
|
-
return f"{self.serialized_name}={self.default_value_declaration}, # type: {
|
|
292
|
-
return f"{self.serialized_name}, # type: {
|
|
322
|
+
return f"{self.serialized_name}={self.default_value_declaration}, # type: {type_annot}"
|
|
323
|
+
return f"{self.serialized_name}, # type: {type_annot}"
|
|
293
324
|
|
|
294
325
|
@property
|
|
295
326
|
def full_serialized_name(self) -> str:
|
|
@@ -298,22 +329,26 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
298
329
|
origin_name = f"self._config.{self.serialized_name}"
|
|
299
330
|
return origin_name
|
|
300
331
|
|
|
301
|
-
@property
|
|
302
|
-
def is_kwarg(self) -> bool:
|
|
303
|
-
# this means "am I in **kwargs?"
|
|
304
|
-
return self.rest_api_name == "Content-Type" or (self.constant and self.rest_api_name != "Accept")
|
|
305
|
-
|
|
306
332
|
@property
|
|
307
333
|
def is_keyword_only(self) -> bool:
|
|
308
334
|
# this means in async mode, I am documented like def hello(positional_1, *, me!)
|
|
309
|
-
return self._keyword_only
|
|
335
|
+
return self._keyword_only or False
|
|
336
|
+
|
|
337
|
+
@is_keyword_only.setter
|
|
338
|
+
def is_keyword_only(self, val: bool) -> None:
|
|
339
|
+
self._keyword_only = val
|
|
340
|
+
self.is_kwarg = False
|
|
310
341
|
|
|
311
342
|
@property
|
|
312
343
|
def is_hidden(self) -> bool:
|
|
313
|
-
return self.serialized_name in _HIDDEN_KWARGS or (
|
|
344
|
+
return self.serialized_name in _HIDDEN_KWARGS and self.is_kwarg or (
|
|
314
345
|
self.yaml_data["implementation"] == "Client" and self.constant
|
|
315
346
|
)
|
|
316
347
|
|
|
348
|
+
@property
|
|
349
|
+
def is_content_type(self) -> bool:
|
|
350
|
+
return self.rest_api_name == "Content-Type" and self.location == ParameterLocation.Header
|
|
351
|
+
|
|
317
352
|
@property
|
|
318
353
|
def is_positional(self) -> bool:
|
|
319
354
|
return self.in_method_signature and not (self.is_keyword_only or self.is_kwarg)
|
|
@@ -327,22 +362,29 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
|
|
|
327
362
|
content_types: Optional[List[str]] = None
|
|
328
363
|
) -> "Parameter":
|
|
329
364
|
http_protocol = yaml_data["protocol"].get("http", {"in": ParameterLocation.Other})
|
|
365
|
+
serialized_name = yaml_data["language"]["python"]["name"]
|
|
366
|
+
schema = get_schema(
|
|
367
|
+
code_model, yaml_data.get("schema"), serialized_name
|
|
368
|
+
)
|
|
369
|
+
target_property = yaml_data.get("targetProperty")
|
|
370
|
+
target_property_name = get_target_property_name(code_model, id(target_property)) if target_property else None
|
|
371
|
+
|
|
330
372
|
return cls(
|
|
331
373
|
code_model=code_model,
|
|
332
374
|
yaml_data=yaml_data,
|
|
333
|
-
schema=
|
|
375
|
+
schema=schema, # FIXME replace by operation model
|
|
334
376
|
# See also https://github.com/Azure/autorest.modelerfour/issues/80
|
|
335
377
|
rest_api_name=yaml_data["language"]["default"].get(
|
|
336
378
|
"serializedName", yaml_data["language"]["default"]["name"]
|
|
337
379
|
),
|
|
338
|
-
serialized_name=
|
|
380
|
+
serialized_name=serialized_name,
|
|
339
381
|
description=yaml_data["language"]["python"]["description"],
|
|
340
382
|
implementation=yaml_data["implementation"],
|
|
341
383
|
required=yaml_data.get("required", False),
|
|
342
384
|
location=ParameterLocation(http_protocol["in"]),
|
|
343
385
|
skip_url_encoding=yaml_data.get("extensions", {}).get("x-ms-skip-url-encoding", False),
|
|
344
386
|
constraints=[], # FIXME constraints
|
|
345
|
-
target_property_name=
|
|
387
|
+
target_property_name=target_property_name,
|
|
346
388
|
style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None,
|
|
347
389
|
explode=http_protocol.get("explode", False),
|
|
348
390
|
grouped_by=yaml_data.get("groupedBy", None),
|
|
@@ -365,6 +407,8 @@ class ParameterOnlyPathAndBodyPositional(Parameter):
|
|
|
365
407
|
|
|
366
408
|
@property
|
|
367
409
|
def is_keyword_only(self) -> bool:
|
|
410
|
+
if self._keyword_only is not None:
|
|
411
|
+
return self._keyword_only
|
|
368
412
|
return self.in_method_signature and not (
|
|
369
413
|
self.is_hidden or
|
|
370
414
|
self.location == ParameterLocation.Path or
|
|
@@ -373,6 +417,11 @@ class ParameterOnlyPathAndBodyPositional(Parameter):
|
|
|
373
417
|
self.is_kwarg
|
|
374
418
|
)
|
|
375
419
|
|
|
420
|
+
@is_keyword_only.setter
|
|
421
|
+
def is_keyword_only(self, val: bool) -> None:
|
|
422
|
+
self._keyword_only = val
|
|
423
|
+
self.is_kwarg = False
|
|
424
|
+
|
|
376
425
|
def get_parameter(code_model):
|
|
377
426
|
if code_model.options["only_path_and_body_params_positional"]:
|
|
378
427
|
return ParameterOnlyPathAndBodyPositional
|