@autorest/python 6.26.7 → 6.27.1
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/generator/pygen/black.py +1 -0
- package/generator/pygen/codegen/__init__.py +19 -0
- package/generator/pygen/codegen/models/lro_paging_operation.py +2 -2
- package/generator/pygen/codegen/models/model_type.py +1 -1
- package/generator/pygen/codegen/models/operation.py +8 -24
- package/generator/pygen/codegen/models/response.py +1 -1
- package/generator/pygen/codegen/serializers/builder_serializer.py +89 -64
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +17 -0
- package/package.json +2 -2
- package/scripts/__pycache__/venvtools.cpython-310.pyc +0 -0
package/generator/pygen/black.py
CHANGED
|
@@ -241,6 +241,23 @@ class CodeGenerator(Plugin):
|
|
|
241
241
|
if not self.options_retriever.is_azure_flavor and self.options_retriever.tracing:
|
|
242
242
|
raise ValueError("Can only have tracing turned on for Azure SDKs.")
|
|
243
243
|
|
|
244
|
+
@staticmethod
|
|
245
|
+
def sort_exceptions(yaml_data: Dict[str, Any]) -> None:
|
|
246
|
+
for client in yaml_data["clients"]:
|
|
247
|
+
for group in client["operationGroups"]:
|
|
248
|
+
for operation in group["operations"]:
|
|
249
|
+
if not operation.get("exceptions"):
|
|
250
|
+
continue
|
|
251
|
+
# sort exceptions by status code, first single status code, then range, then default
|
|
252
|
+
operation["exceptions"] = sorted(
|
|
253
|
+
operation["exceptions"],
|
|
254
|
+
key=lambda x: (
|
|
255
|
+
3
|
|
256
|
+
if x["statusCodes"][0] == "default"
|
|
257
|
+
else (1 if isinstance(x["statusCodes"][0], int) else 2)
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
|
|
244
261
|
@staticmethod
|
|
245
262
|
def remove_cloud_errors(yaml_data: Dict[str, Any]) -> None:
|
|
246
263
|
for client in yaml_data["clients"]:
|
|
@@ -316,6 +333,8 @@ class CodeGenerator(Plugin):
|
|
|
316
333
|
options = self._build_code_model_options()
|
|
317
334
|
yaml_data = self.get_yaml()
|
|
318
335
|
|
|
336
|
+
self.sort_exceptions(yaml_data)
|
|
337
|
+
|
|
319
338
|
if self.options_retriever.azure_arm:
|
|
320
339
|
self.remove_cloud_errors(yaml_data)
|
|
321
340
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, List, Union
|
|
7
7
|
from .imports import FileImport
|
|
8
8
|
from .lro_operation import LROOperationBase
|
|
9
9
|
from .paging_operation import PagingOperationBase
|
|
@@ -12,7 +12,7 @@ from .response import LROPagingResponse, Response
|
|
|
12
12
|
|
|
13
13
|
class LROPagingOperation(LROOperationBase[LROPagingResponse], PagingOperationBase[LROPagingResponse]):
|
|
14
14
|
@property
|
|
15
|
-
def success_status_codes(self):
|
|
15
|
+
def success_status_codes(self) -> List[Union[int, str, List[int]]]:
|
|
16
16
|
"""The list of all successfull status code."""
|
|
17
17
|
return [200]
|
|
18
18
|
|
|
@@ -348,7 +348,7 @@ class DPGModelType(GeneratedModelType):
|
|
|
348
348
|
|
|
349
349
|
@property
|
|
350
350
|
def instance_check_template(self) -> str:
|
|
351
|
-
return "isinstance({},
|
|
351
|
+
return "isinstance({}, " + f"_models.{self.name})"
|
|
352
352
|
|
|
353
353
|
def imports(self, **kwargs: Any) -> FileImport:
|
|
354
354
|
file_import = super().imports(**kwargs)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
-
from itertools import chain
|
|
7
6
|
from typing import (
|
|
8
7
|
Dict,
|
|
9
8
|
List,
|
|
@@ -14,7 +13,6 @@ from typing import (
|
|
|
14
13
|
Generic,
|
|
15
14
|
TypeVar,
|
|
16
15
|
cast,
|
|
17
|
-
Sequence,
|
|
18
16
|
)
|
|
19
17
|
|
|
20
18
|
from .request_builder_parameter import RequestBuilderParameter
|
|
@@ -201,17 +199,13 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
201
199
|
exception_schema = default_exceptions[0].type
|
|
202
200
|
if isinstance(exception_schema, ModelType):
|
|
203
201
|
return exception_schema.type_annotation(skip_quote=True)
|
|
204
|
-
|
|
205
|
-
return "'object'"
|
|
202
|
+
return None if self.code_model.options["models_mode"] == "dpg" else "'object'"
|
|
206
203
|
|
|
207
204
|
@property
|
|
208
205
|
def non_default_errors(self) -> List[Response]:
|
|
209
|
-
return [
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def non_default_error_status_codes(self) -> List[Union[str, int]]:
|
|
213
|
-
"""Actually returns all of the status codes from exceptions (besides default)"""
|
|
214
|
-
return list(chain.from_iterable([error.status_codes for error in self.non_default_errors]))
|
|
206
|
+
return [
|
|
207
|
+
e for e in self.exceptions if "default" not in e.status_codes and e.type and isinstance(e.type, ModelType)
|
|
208
|
+
]
|
|
215
209
|
|
|
216
210
|
def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport: # pylint: disable=unused-argument
|
|
217
211
|
file_import = FileImport(self.code_model)
|
|
@@ -344,19 +338,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
344
338
|
file_import.add_submodule_import("exceptions", error, ImportType.SDKCORE)
|
|
345
339
|
if self.code_model.options["azure_arm"]:
|
|
346
340
|
file_import.add_submodule_import("azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.SDKCORE)
|
|
347
|
-
if self.non_default_errors:
|
|
348
|
-
file_import.add_submodule_import(
|
|
349
|
-
"typing",
|
|
350
|
-
"Type",
|
|
351
|
-
ImportType.STDLIB,
|
|
352
|
-
)
|
|
353
341
|
file_import.add_mutable_mapping_import()
|
|
354
|
-
if self.non_default_error_status_codes:
|
|
355
|
-
file_import.add_submodule_import(
|
|
356
|
-
"typing",
|
|
357
|
-
"cast",
|
|
358
|
-
ImportType.STDLIB,
|
|
359
|
-
)
|
|
360
342
|
|
|
361
343
|
if self.has_kwargs_to_pop_with_default(
|
|
362
344
|
self.parameters.kwargs_to_pop, ParameterLocation.HEADER # type: ignore
|
|
@@ -436,7 +418,9 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
436
418
|
elif any(r.type for r in self.responses):
|
|
437
419
|
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
|
|
438
420
|
if self.default_error_deserialization or self.non_default_errors:
|
|
439
|
-
file_import.add_submodule_import(
|
|
421
|
+
file_import.add_submodule_import(
|
|
422
|
+
f"{relative_path}_model_base", "_failsafe_deserialize", ImportType.LOCAL
|
|
423
|
+
)
|
|
440
424
|
return file_import
|
|
441
425
|
|
|
442
426
|
def get_response_from_status(self, status_code: Optional[Union[str, int]]) -> ResponseType:
|
|
@@ -446,7 +430,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
446
430
|
raise ValueError(f"Incorrect status code {status_code}, operation {self.name}") from exc
|
|
447
431
|
|
|
448
432
|
@property
|
|
449
|
-
def success_status_codes(self) ->
|
|
433
|
+
def success_status_codes(self) -> List[Union[int, str, List[int]]]:
|
|
450
434
|
"""The list of all successfull status code."""
|
|
451
435
|
return sorted([code for response in self.responses for code in response.status_codes])
|
|
452
436
|
|
|
@@ -54,7 +54,7 @@ class Response(BaseModel):
|
|
|
54
54
|
type: Optional[BaseType] = None,
|
|
55
55
|
) -> None:
|
|
56
56
|
super().__init__(yaml_data=yaml_data, code_model=code_model)
|
|
57
|
-
self.status_codes: List[Union[int, str]] = yaml_data["statusCodes"]
|
|
57
|
+
self.status_codes: List[Union[int, str, List[int]]] = yaml_data["statusCodes"]
|
|
58
58
|
self.headers = headers or []
|
|
59
59
|
self.type = type
|
|
60
60
|
self.nullable = yaml_data.get("nullable")
|
|
@@ -61,14 +61,6 @@ def _all_same(data: List[List[str]]) -> bool:
|
|
|
61
61
|
return len(data) > 1 and all(sorted(data[0]) == sorted(data[i]) for i in range(1, len(data)))
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def _need_type_ignore(builder: OperationType) -> bool:
|
|
65
|
-
for e in builder.non_default_errors:
|
|
66
|
-
for status_code in e.status_codes:
|
|
67
|
-
if status_code in (401, 404, 409, 304):
|
|
68
|
-
return True
|
|
69
|
-
return False
|
|
70
|
-
|
|
71
|
-
|
|
72
64
|
def _xml_config(send_xml: bool, content_types: List[str]) -> str:
|
|
73
65
|
if not (send_xml and "xml" in str(content_types)):
|
|
74
66
|
return ""
|
|
@@ -999,20 +991,82 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
999
991
|
elif isinstance(builder.stream_value, str): # _stream is not sure, so we need to judge it
|
|
1000
992
|
retval.append(" if _stream:")
|
|
1001
993
|
retval.extend([f" {l}" for l in response_read])
|
|
1002
|
-
|
|
1003
|
-
retval.append(
|
|
1004
|
-
f" map_error(status_code=response.status_code, response=response, error_map=error_map){type_ignore}"
|
|
1005
|
-
)
|
|
994
|
+
retval.append(" map_error(status_code=response.status_code, response=response, error_map=error_map)")
|
|
1006
995
|
error_model = ""
|
|
996
|
+
if builder.non_default_errors and self.code_model.options["models_mode"]:
|
|
997
|
+
error_model = ", model=error"
|
|
998
|
+
condition = "if"
|
|
999
|
+
retval.append(" error = None")
|
|
1000
|
+
for e in builder.non_default_errors:
|
|
1001
|
+
# single status code
|
|
1002
|
+
if isinstance(e.status_codes[0], int):
|
|
1003
|
+
for status_code in e.status_codes:
|
|
1004
|
+
retval.append(f" {condition} response.status_code == {status_code}:")
|
|
1005
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
1006
|
+
retval.append(f" error = _failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, response.json())") # type: ignore # pylint: disable=line-too-long
|
|
1007
|
+
else:
|
|
1008
|
+
retval.append(
|
|
1009
|
+
f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, " # type: ignore # pylint: disable=line-too-long
|
|
1010
|
+
"pipeline_response)"
|
|
1011
|
+
)
|
|
1012
|
+
# add build-in error type
|
|
1013
|
+
# TODO: we should decide whether need to this wrapper for customized error type
|
|
1014
|
+
if status_code == 401:
|
|
1015
|
+
retval.append(
|
|
1016
|
+
" raise ClientAuthenticationError(response=response{}{})".format(
|
|
1017
|
+
error_model,
|
|
1018
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1019
|
+
)
|
|
1020
|
+
)
|
|
1021
|
+
elif status_code == 404:
|
|
1022
|
+
retval.append(
|
|
1023
|
+
" raise ResourceNotFoundError(response=response{}{})".format(
|
|
1024
|
+
error_model,
|
|
1025
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1026
|
+
)
|
|
1027
|
+
)
|
|
1028
|
+
elif status_code == 409:
|
|
1029
|
+
retval.append(
|
|
1030
|
+
" raise ResourceExistsError(response=response{}{})".format(
|
|
1031
|
+
error_model,
|
|
1032
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1033
|
+
)
|
|
1034
|
+
)
|
|
1035
|
+
elif status_code == 304:
|
|
1036
|
+
retval.append(
|
|
1037
|
+
" raise ResourceNotModifiedError(response=response{}{})".format(
|
|
1038
|
+
error_model,
|
|
1039
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1040
|
+
)
|
|
1041
|
+
)
|
|
1042
|
+
# ranged status code only exist in typespec and will not have multiple status codes
|
|
1043
|
+
else:
|
|
1044
|
+
retval.append(
|
|
1045
|
+
f" {condition} {e.status_codes[0][0]} <= response.status_code <= {e.status_codes[0][1]}:"
|
|
1046
|
+
)
|
|
1047
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
1048
|
+
retval.append(f" error = _failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, response.json())") # type: ignore # pylint: disable=line-too-long
|
|
1049
|
+
else:
|
|
1050
|
+
retval.append(
|
|
1051
|
+
f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, " # type: ignore # pylint: disable=line-too-long
|
|
1052
|
+
"pipeline_response)"
|
|
1053
|
+
)
|
|
1054
|
+
condition = "elif"
|
|
1055
|
+
# default error handling
|
|
1007
1056
|
if builder.default_error_deserialization and self.code_model.options["models_mode"]:
|
|
1057
|
+
error_model = ", model=error"
|
|
1058
|
+
indent = " " if builder.non_default_errors else " "
|
|
1059
|
+
if builder.non_default_errors:
|
|
1060
|
+
retval.append(" else:")
|
|
1008
1061
|
if self.code_model.options["models_mode"] == "dpg":
|
|
1009
|
-
retval.append(
|
|
1062
|
+
retval.append(
|
|
1063
|
+
f"{indent}error = _failsafe_deserialize({builder.default_error_deserialization}, response.json())"
|
|
1064
|
+
)
|
|
1010
1065
|
else:
|
|
1011
1066
|
retval.append(
|
|
1012
|
-
f"
|
|
1067
|
+
f"{indent}error = self._deserialize.failsafe_deserialize({builder.default_error_deserialization}, "
|
|
1013
1068
|
"pipeline_response)"
|
|
1014
1069
|
)
|
|
1015
|
-
error_model = ", model=error"
|
|
1016
1070
|
retval.append(
|
|
1017
1071
|
" raise HttpResponseError(response=response{}{})".format(
|
|
1018
1072
|
error_model,
|
|
@@ -1034,7 +1088,7 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
1034
1088
|
if len(builder.responses) > 1:
|
|
1035
1089
|
status_codes, res_headers, res_deserialization = [], [], []
|
|
1036
1090
|
for status_code in builder.success_status_codes:
|
|
1037
|
-
response = builder.get_response_from_status(status_code)
|
|
1091
|
+
response = builder.get_response_from_status(status_code) # type: ignore
|
|
1038
1092
|
if response.headers or response.type:
|
|
1039
1093
|
status_codes.append(status_code)
|
|
1040
1094
|
res_headers.append(self.response_headers(response))
|
|
@@ -1085,60 +1139,31 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
1085
1139
|
retval.append("return 200 <= response.status_code <= 299")
|
|
1086
1140
|
return retval
|
|
1087
1141
|
|
|
1142
|
+
def _need_specific_error_map(self, code: int, builder: OperationType) -> bool:
|
|
1143
|
+
for non_default_error in builder.non_default_errors:
|
|
1144
|
+
# single status code
|
|
1145
|
+
if code in non_default_error.status_codes:
|
|
1146
|
+
return False
|
|
1147
|
+
# ranged status code
|
|
1148
|
+
if (
|
|
1149
|
+
isinstance(non_default_error.status_codes[0], list)
|
|
1150
|
+
and non_default_error.status_codes[0][0] <= code <= non_default_error.status_codes[0][1]
|
|
1151
|
+
):
|
|
1152
|
+
return False
|
|
1153
|
+
return True
|
|
1154
|
+
|
|
1088
1155
|
def error_map(self, builder: OperationType) -> List[str]:
|
|
1089
1156
|
retval = ["error_map: MutableMapping = {"]
|
|
1090
|
-
if builder.non_default_errors:
|
|
1091
|
-
|
|
1157
|
+
if builder.non_default_errors and self.code_model.options["models_mode"]:
|
|
1158
|
+
# TODO: we should decide whether to add the build-in error map when there is a customized default error type
|
|
1159
|
+
if self._need_specific_error_map(401, builder):
|
|
1092
1160
|
retval.append(" 401: ClientAuthenticationError,")
|
|
1093
|
-
if
|
|
1161
|
+
if self._need_specific_error_map(404, builder):
|
|
1094
1162
|
retval.append(" 404: ResourceNotFoundError,")
|
|
1095
|
-
if
|
|
1163
|
+
if self._need_specific_error_map(409, builder):
|
|
1096
1164
|
retval.append(" 409: ResourceExistsError,")
|
|
1097
|
-
if
|
|
1165
|
+
if self._need_specific_error_map(304, builder):
|
|
1098
1166
|
retval.append(" 304: ResourceNotModifiedError,")
|
|
1099
|
-
for e in builder.non_default_errors:
|
|
1100
|
-
error_model_str = ""
|
|
1101
|
-
if isinstance(e.type, ModelType):
|
|
1102
|
-
if self.code_model.options["models_mode"] == "msrest":
|
|
1103
|
-
error_model_str = (
|
|
1104
|
-
f", model=self._deserialize(" f"_models.{e.type.serialization_type}, response)"
|
|
1105
|
-
)
|
|
1106
|
-
elif self.code_model.options["models_mode"] == "dpg":
|
|
1107
|
-
error_model_str = f", model=_deserialize(_models.{e.type.name}, response.json())"
|
|
1108
|
-
error_format_str = ", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""
|
|
1109
|
-
for status_code in e.status_codes:
|
|
1110
|
-
if status_code == 401:
|
|
1111
|
-
retval.append(
|
|
1112
|
-
" 401: cast(Type[HttpResponseError], "
|
|
1113
|
-
"lambda response: ClientAuthenticationError(response=response"
|
|
1114
|
-
f"{error_model_str}{error_format_str})),"
|
|
1115
|
-
)
|
|
1116
|
-
elif status_code == 404:
|
|
1117
|
-
retval.append(
|
|
1118
|
-
" 404: cast(Type[HttpResponseError], "
|
|
1119
|
-
"lambda response: ResourceNotFoundError(response=response"
|
|
1120
|
-
f"{error_model_str}{error_format_str})),"
|
|
1121
|
-
)
|
|
1122
|
-
elif status_code == 409:
|
|
1123
|
-
retval.append(
|
|
1124
|
-
" 409: cast(Type[HttpResponseError], "
|
|
1125
|
-
"lambda response: ResourceExistsError(response=response"
|
|
1126
|
-
f"{error_model_str}{error_format_str})),"
|
|
1127
|
-
)
|
|
1128
|
-
elif status_code == 304:
|
|
1129
|
-
retval.append(
|
|
1130
|
-
" 304: cast(Type[HttpResponseError], "
|
|
1131
|
-
"lambda response: ResourceNotModifiedError(response=response"
|
|
1132
|
-
f"{error_model_str}{error_format_str})),"
|
|
1133
|
-
)
|
|
1134
|
-
elif not error_model_str and not error_format_str:
|
|
1135
|
-
retval.append(f" {status_code}: HttpResponseError,")
|
|
1136
|
-
else:
|
|
1137
|
-
retval.append(
|
|
1138
|
-
f" {status_code}: cast(Type[HttpResponseError], "
|
|
1139
|
-
"lambda response: HttpResponseError(response=response"
|
|
1140
|
-
f"{error_model_str}{error_format_str})),"
|
|
1141
|
-
)
|
|
1142
1167
|
else:
|
|
1143
1168
|
retval.append(
|
|
1144
1169
|
" 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, "
|
|
@@ -892,6 +892,23 @@ def _deserialize(
|
|
|
892
892
|
return _deserialize_with_callable(deserializer, value)
|
|
893
893
|
|
|
894
894
|
|
|
895
|
+
def _failsafe_deserialize(
|
|
896
|
+
deserializer: typing.Any,
|
|
897
|
+
value: typing.Any,
|
|
898
|
+
module: typing.Optional[str] = None,
|
|
899
|
+
rf: typing.Optional["_RestField"] = None,
|
|
900
|
+
format: typing.Optional[str] = None,
|
|
901
|
+
) -> typing.Any:
|
|
902
|
+
try:
|
|
903
|
+
return _deserialize(deserializer, value, module, rf, format)
|
|
904
|
+
except DeserializationError:
|
|
905
|
+
_LOGGER.warning(
|
|
906
|
+
"Ran into a deserialization error. Ignoring since this is failsafe deserialization",
|
|
907
|
+
exc_info=True
|
|
908
|
+
)
|
|
909
|
+
return None
|
|
910
|
+
|
|
911
|
+
|
|
895
912
|
class _RestField:
|
|
896
913
|
def __init__(
|
|
897
914
|
self,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autorest/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.27.1",
|
|
4
4
|
"description": "The Python extension for generators in AutoRest.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@typespec/http-client-python": "~0.3
|
|
22
|
+
"@typespec/http-client-python": "~0.4.3",
|
|
23
23
|
"@autorest/system-requirements": "~1.0.2",
|
|
24
24
|
"fs-extra": "~11.2.0",
|
|
25
25
|
"tsx": "~4.19.1"
|
|
Binary file
|