@autorest/python 6.26.7 → 6.27.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/generator/pygen/codegen/__init__.py +12 -0
- package/generator/pygen/codegen/models/model_type.py +1 -1
- package/generator/pygen/codegen/models/operation.py +4 -21
- package/generator/pygen/codegen/models/response.py +1 -1
- package/generator/pygen/codegen/serializers/builder_serializer.py +81 -61
- 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
|
@@ -241,6 +241,16 @@ 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(operation["exceptions"], key=lambda x: 3 if x["statusCodes"][0] == "default" else (1 if isinstance(x["statusCodes"][0], int) else 2))
|
|
253
|
+
|
|
244
254
|
@staticmethod
|
|
245
255
|
def remove_cloud_errors(yaml_data: Dict[str, Any]) -> None:
|
|
246
256
|
for client in yaml_data["clients"]:
|
|
@@ -315,6 +325,8 @@ class CodeGenerator(Plugin):
|
|
|
315
325
|
self._validate_code_model_options()
|
|
316
326
|
options = self._build_code_model_options()
|
|
317
327
|
yaml_data = self.get_yaml()
|
|
328
|
+
|
|
329
|
+
self.sort_exceptions(yaml_data)
|
|
318
330
|
|
|
319
331
|
if self.options_retriever.azure_arm:
|
|
320
332
|
self.remove_cloud_errors(yaml_data)
|
|
@@ -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)
|
|
@@ -9,6 +9,7 @@ from typing import (
|
|
|
9
9
|
List,
|
|
10
10
|
Any,
|
|
11
11
|
Optional,
|
|
12
|
+
Tuple,
|
|
12
13
|
Union,
|
|
13
14
|
TYPE_CHECKING,
|
|
14
15
|
Generic,
|
|
@@ -201,17 +202,11 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
201
202
|
exception_schema = default_exceptions[0].type
|
|
202
203
|
if isinstance(exception_schema, ModelType):
|
|
203
204
|
return exception_schema.type_annotation(skip_quote=True)
|
|
204
|
-
|
|
205
|
-
return "'object'"
|
|
205
|
+
return None if self.code_model.options["models_mode"] == "dpg" else "'object'"
|
|
206
206
|
|
|
207
207
|
@property
|
|
208
208
|
def non_default_errors(self) -> List[Response]:
|
|
209
|
-
return [e for e in self.exceptions if "default" not in e.status_codes]
|
|
210
|
-
|
|
211
|
-
@property
|
|
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]))
|
|
209
|
+
return [e for e in self.exceptions if "default" not in e.status_codes and e.type and isinstance(e.type, ModelType)]
|
|
215
210
|
|
|
216
211
|
def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport: # pylint: disable=unused-argument
|
|
217
212
|
file_import = FileImport(self.code_model)
|
|
@@ -344,19 +339,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
344
339
|
file_import.add_submodule_import("exceptions", error, ImportType.SDKCORE)
|
|
345
340
|
if self.code_model.options["azure_arm"]:
|
|
346
341
|
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
342
|
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
343
|
|
|
361
344
|
if self.has_kwargs_to_pop_with_default(
|
|
362
345
|
self.parameters.kwargs_to_pop, ParameterLocation.HEADER # type: ignore
|
|
@@ -436,7 +419,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
436
419
|
elif any(r.type for r in self.responses):
|
|
437
420
|
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
|
|
438
421
|
if self.default_error_deserialization or self.non_default_errors:
|
|
439
|
-
file_import.add_submodule_import(f"{relative_path}_model_base", "
|
|
422
|
+
file_import.add_submodule_import(f"{relative_path}_model_base", "_failsafe_deserialize", ImportType.LOCAL)
|
|
440
423
|
return file_import
|
|
441
424
|
|
|
442
425
|
def get_response_from_status(self, status_code: Optional[Union[str, int]]) -> ResponseType:
|
|
@@ -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,80 @@ 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
|
-
type_ignore = " # type: ignore" if _need_type_ignore(builder) else ""
|
|
1003
994
|
retval.append(
|
|
1004
|
-
f" map_error(status_code=response.status_code, response=response, error_map=error_map)
|
|
995
|
+
f" map_error(status_code=response.status_code, response=response, error_map=error_map)"
|
|
1005
996
|
)
|
|
1006
997
|
error_model = ""
|
|
998
|
+
if builder.non_default_errors and self.code_model.options["models_mode"]:
|
|
999
|
+
error_model = ", model=error"
|
|
1000
|
+
condition = "if"
|
|
1001
|
+
retval.append(" error = None")
|
|
1002
|
+
for e in builder.non_default_errors:
|
|
1003
|
+
# single status code
|
|
1004
|
+
if isinstance(e.status_codes[0], int):
|
|
1005
|
+
for status_code in e.status_codes:
|
|
1006
|
+
retval.append(f" {condition} response.status_code == {status_code}:")
|
|
1007
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
1008
|
+
retval.append(f" error = _failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, response.json())")
|
|
1009
|
+
else:
|
|
1010
|
+
retval.append(
|
|
1011
|
+
f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, "
|
|
1012
|
+
"pipeline_response)"
|
|
1013
|
+
)
|
|
1014
|
+
# add build-in error type
|
|
1015
|
+
# TODO: we should decide whether need to this wrapper for customized error type
|
|
1016
|
+
if status_code == 401:
|
|
1017
|
+
retval.append(
|
|
1018
|
+
" raise ClientAuthenticationError(response=response{}{})".format(
|
|
1019
|
+
error_model,
|
|
1020
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1021
|
+
)
|
|
1022
|
+
)
|
|
1023
|
+
elif status_code == 404:
|
|
1024
|
+
retval.append(
|
|
1025
|
+
" raise ResourceNotFoundError(response=response{}{})".format(
|
|
1026
|
+
error_model,
|
|
1027
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1028
|
+
)
|
|
1029
|
+
)
|
|
1030
|
+
elif status_code == 409:
|
|
1031
|
+
retval.append(
|
|
1032
|
+
" raise ResourceExistsError(response=response{}{})".format(
|
|
1033
|
+
error_model,
|
|
1034
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1035
|
+
)
|
|
1036
|
+
)
|
|
1037
|
+
elif status_code == 304:
|
|
1038
|
+
retval.append(
|
|
1039
|
+
" raise ResourceNotModifiedError(response=response{}{})".format(
|
|
1040
|
+
error_model,
|
|
1041
|
+
(", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
|
|
1042
|
+
)
|
|
1043
|
+
)
|
|
1044
|
+
# ranged status code only exist in typespec and will not have multiple status codes
|
|
1045
|
+
else:
|
|
1046
|
+
retval.append(f" {condition} {e.status_codes[0][0]} <= response.status_code <= {e.status_codes[0][1]}:")
|
|
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())")
|
|
1049
|
+
else:
|
|
1050
|
+
retval.append(
|
|
1051
|
+
f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, "
|
|
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(f"
|
|
1062
|
+
retval.append(f"{indent}error = _failsafe_deserialize({builder.default_error_deserialization}, response.json())")
|
|
1010
1063
|
else:
|
|
1011
1064
|
retval.append(
|
|
1012
|
-
f"
|
|
1065
|
+
f"{indent}error = self._deserialize.failsafe_deserialize({builder.default_error_deserialization}, "
|
|
1013
1066
|
"pipeline_response)"
|
|
1014
1067
|
)
|
|
1015
|
-
error_model = ", model=error"
|
|
1016
1068
|
retval.append(
|
|
1017
1069
|
" raise HttpResponseError(response=response{}{})".format(
|
|
1018
1070
|
error_model,
|
|
@@ -1085,60 +1137,28 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
1085
1137
|
retval.append("return 200 <= response.status_code <= 299")
|
|
1086
1138
|
return retval
|
|
1087
1139
|
|
|
1140
|
+
def _need_specific_error_map(self, code: int, builder: OperationType) -> bool:
|
|
1141
|
+
for non_default_error in builder.non_default_errors:
|
|
1142
|
+
# single status code
|
|
1143
|
+
if code in non_default_error.status_codes:
|
|
1144
|
+
return False
|
|
1145
|
+
# ranged status code
|
|
1146
|
+
if isinstance(non_default_error.status_codes[0], list) and non_default_error.status_codes[0][0] <= code <= non_default_error.status_codes[0][1]:
|
|
1147
|
+
return False
|
|
1148
|
+
return True
|
|
1149
|
+
|
|
1088
1150
|
def error_map(self, builder: OperationType) -> List[str]:
|
|
1089
1151
|
retval = ["error_map: MutableMapping = {"]
|
|
1090
|
-
if builder.non_default_errors:
|
|
1091
|
-
|
|
1152
|
+
if builder.non_default_errors and self.code_model.options["models_mode"]:
|
|
1153
|
+
# TODO: we should decide whether to add the build-in error map when there is a customized default error type
|
|
1154
|
+
if self._need_specific_error_map(401, builder):
|
|
1092
1155
|
retval.append(" 401: ClientAuthenticationError,")
|
|
1093
|
-
if
|
|
1156
|
+
if self._need_specific_error_map(404, builder):
|
|
1094
1157
|
retval.append(" 404: ResourceNotFoundError,")
|
|
1095
|
-
if
|
|
1158
|
+
if self._need_specific_error_map(409, builder):
|
|
1096
1159
|
retval.append(" 409: ResourceExistsError,")
|
|
1097
|
-
if
|
|
1160
|
+
if self._need_specific_error_map(304, builder):
|
|
1098
1161
|
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
1162
|
else:
|
|
1143
1163
|
retval.append(
|
|
1144
1164
|
" 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.0",
|
|
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.
|
|
22
|
+
"@typespec/http-client-python": "~0.4.2",
|
|
23
23
|
"@autorest/system-requirements": "~1.0.2",
|
|
24
24
|
"fs-extra": "~11.2.0",
|
|
25
25
|
"tsx": "~4.19.1"
|
|
Binary file
|