@autorest/python 5.16.0 → 5.19.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.
Files changed (96) hide show
  1. package/ChangeLog.md +79 -4
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +1 -1
  4. package/autorest/codegen/__init__.py +55 -211
  5. package/autorest/codegen/models/__init__.py +116 -83
  6. package/autorest/codegen/models/base_builder.py +49 -88
  7. package/autorest/codegen/models/base_model.py +1 -1
  8. package/autorest/codegen/models/{base_schema.py → base_type.py} +61 -39
  9. package/autorest/codegen/models/client.py +165 -53
  10. package/autorest/codegen/models/code_model.py +122 -257
  11. package/autorest/codegen/models/combined_type.py +107 -0
  12. package/autorest/codegen/models/{constant_schema.py → constant_type.py} +49 -40
  13. package/autorest/codegen/models/credential_types.py +224 -0
  14. package/autorest/codegen/models/dictionary_type.py +131 -0
  15. package/autorest/codegen/models/enum_type.py +195 -0
  16. package/autorest/codegen/models/imports.py +80 -2
  17. package/autorest/codegen/models/list_type.py +149 -0
  18. package/autorest/codegen/models/lro_operation.py +79 -156
  19. package/autorest/codegen/models/lro_paging_operation.py +28 -11
  20. package/autorest/codegen/models/model_type.py +262 -0
  21. package/autorest/codegen/models/operation.py +331 -298
  22. package/autorest/codegen/models/operation_group.py +54 -91
  23. package/autorest/codegen/models/paging_operation.py +82 -123
  24. package/autorest/codegen/models/parameter.py +289 -396
  25. package/autorest/codegen/models/parameter_list.py +355 -360
  26. package/autorest/codegen/models/primitive_types.py +544 -0
  27. package/autorest/codegen/models/property.py +123 -139
  28. package/autorest/codegen/models/request_builder.py +130 -102
  29. package/autorest/codegen/models/request_builder_parameter.py +112 -100
  30. package/autorest/codegen/models/response.py +325 -0
  31. package/autorest/codegen/models/utils.py +12 -19
  32. package/autorest/codegen/serializers/__init__.py +55 -37
  33. package/autorest/codegen/serializers/builder_serializer.py +695 -1144
  34. package/autorest/codegen/serializers/client_serializer.py +92 -89
  35. package/autorest/codegen/serializers/general_serializer.py +15 -69
  36. package/autorest/codegen/serializers/import_serializer.py +7 -4
  37. package/autorest/codegen/serializers/metadata_serializer.py +15 -104
  38. package/autorest/codegen/serializers/model_base_serializer.py +49 -36
  39. package/autorest/codegen/serializers/model_generic_serializer.py +8 -6
  40. package/autorest/codegen/serializers/model_init_serializer.py +2 -4
  41. package/autorest/codegen/serializers/model_python3_serializer.py +22 -16
  42. package/autorest/codegen/serializers/operation_groups_serializer.py +7 -13
  43. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  44. package/autorest/codegen/serializers/request_builders_serializer.py +13 -30
  45. package/autorest/codegen/serializers/utils.py +0 -140
  46. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  47. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +10 -7
  48. package/autorest/codegen/templates/config.py.jinja2 +13 -13
  49. package/autorest/codegen/templates/enum.py.jinja2 +4 -4
  50. package/autorest/codegen/templates/enum_container.py.jinja2 +1 -1
  51. package/autorest/codegen/templates/init.py.jinja2 +2 -2
  52. package/autorest/codegen/templates/lro_operation.py.jinja2 +4 -1
  53. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +4 -1
  54. package/autorest/codegen/templates/metadata.json.jinja2 +33 -33
  55. package/autorest/codegen/templates/model.py.jinja2 +23 -24
  56. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  57. package/autorest/codegen/templates/model_init.py.jinja2 +3 -5
  58. package/autorest/codegen/templates/operation.py.jinja2 +6 -8
  59. package/autorest/codegen/templates/operation_group.py.jinja2 +21 -8
  60. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +2 -2
  61. package/autorest/codegen/templates/operation_tools.jinja2 +11 -3
  62. package/autorest/codegen/templates/paging_operation.py.jinja2 +2 -2
  63. package/autorest/codegen/templates/request_builder.py.jinja2 +10 -15
  64. package/autorest/codegen/templates/request_builders.py.jinja2 +1 -1
  65. package/autorest/codegen/templates/serialization.py.jinja2 +2006 -0
  66. package/autorest/codegen/templates/setup.py.jinja2 +13 -3
  67. package/autorest/codegen/templates/vendor.py.jinja2 +11 -1
  68. package/autorest/jsonrpc/server.py +15 -3
  69. package/autorest/m4reformatter/__init__.py +1126 -0
  70. package/autorest/multiapi/models/client.py +12 -2
  71. package/autorest/multiapi/models/code_model.py +1 -1
  72. package/autorest/multiapi/serializers/__init__.py +18 -4
  73. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  74. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  75. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  76. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  77. package/autorest/postprocess/__init__.py +202 -0
  78. package/autorest/postprocess/get_all.py +19 -0
  79. package/autorest/postprocess/venvtools.py +73 -0
  80. package/autorest/preprocess/__init__.py +210 -0
  81. package/autorest/preprocess/helpers.py +54 -0
  82. package/autorest/{namer → preprocess}/python_mappings.py +21 -16
  83. package/package.json +2 -2
  84. package/autorest/codegen/models/credential_model.py +0 -55
  85. package/autorest/codegen/models/credential_schema.py +0 -95
  86. package/autorest/codegen/models/credential_schema_policy.py +0 -73
  87. package/autorest/codegen/models/dictionary_schema.py +0 -106
  88. package/autorest/codegen/models/enum_schema.py +0 -225
  89. package/autorest/codegen/models/list_schema.py +0 -135
  90. package/autorest/codegen/models/object_schema.py +0 -303
  91. package/autorest/codegen/models/primitive_schemas.py +0 -495
  92. package/autorest/codegen/models/request_builder_parameter_list.py +0 -249
  93. package/autorest/codegen/models/schema_request.py +0 -55
  94. package/autorest/codegen/models/schema_response.py +0 -141
  95. package/autorest/namer/__init__.py +0 -23
  96. package/autorest/namer/name_converter.py +0 -509
@@ -3,287 +3,346 @@
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
  import logging
8
- from typing import cast, Dict, List, Any, Optional, Union, Set, TYPE_CHECKING
7
+ from itertools import chain
8
+ from typing import (
9
+ Dict,
10
+ List,
11
+ Any,
12
+ Optional,
13
+ Union,
14
+ TYPE_CHECKING,
15
+ Generic,
16
+ TypeVar,
17
+ cast,
18
+ )
19
+
20
+ from .request_builder_parameter import RequestBuilderParameter
21
+
22
+ from .utils import OrderedSet, add_to_pylint_disable
9
23
 
10
- from .base_builder import BaseBuilder, create_parameters
24
+ from .base_builder import BaseBuilder
11
25
  from .imports import FileImport, ImportType, TypingSection
12
- from .schema_response import SchemaResponse
26
+ from .response import (
27
+ Response,
28
+ PagingResponse,
29
+ LROResponse,
30
+ LROPagingResponse,
31
+ get_response,
32
+ )
13
33
  from .parameter import (
34
+ BodyParameter,
35
+ MultipartBodyParameter,
14
36
  Parameter,
15
- ParameterMethodLocation,
16
- get_parameter,
17
37
  ParameterLocation,
18
38
  )
19
39
  from .parameter_list import ParameterList
20
- from .base_schema import BaseSchema
21
- from .object_schema import ObjectSchema
22
- from .request_builder import RequestBuilder
23
- from .schema_request import SchemaRequest
24
- from .primitive_schemas import IOSchema
40
+ from .model_type import ModelType
41
+ from .request_builder import OverloadedRequestBuilder, RequestBuilder
25
42
 
26
43
  if TYPE_CHECKING:
27
44
  from .code_model import CodeModel
28
45
 
29
46
  _LOGGER = logging.getLogger(__name__)
30
47
 
48
+ ResponseType = TypeVar(
49
+ "ResponseType",
50
+ bound=Union[Response, PagingResponse, LROResponse, LROPagingResponse],
51
+ )
31
52
 
32
- class Operation(
33
- BaseBuilder
34
- ): # pylint: disable=too-many-public-methods, too-many-instance-attributes
35
- """Represent an self."""
36
53
 
54
+ class OperationBase( # pylint: disable=too-many-public-methods
55
+ Generic[ResponseType], BaseBuilder[ParameterList]
56
+ ):
37
57
  def __init__(
38
58
  self,
39
59
  yaml_data: Dict[str, Any],
40
60
  code_model: "CodeModel",
41
- request_builder: RequestBuilder,
42
61
  name: str,
43
- description: str,
44
- api_versions: Set[str],
62
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
45
63
  parameters: ParameterList,
46
- multiple_content_type_parameters: ParameterList,
47
- schema_requests: List[SchemaRequest],
48
- summary: Optional[str] = None,
49
- responses: Optional[List[SchemaResponse]] = None,
50
- exceptions: Optional[List[SchemaResponse]] = None,
51
- want_description_docstring: bool = True,
52
- want_tracing: bool = True,
64
+ responses: List[ResponseType],
65
+ exceptions: List[Response],
53
66
  *,
67
+ overloads: Optional[List["Operation"]] = None,
68
+ public: bool = True,
69
+ want_tracing: bool = True,
54
70
  abstract: bool = False,
55
71
  ) -> None:
56
72
  super().__init__(
57
73
  code_model=code_model,
58
74
  yaml_data=yaml_data,
59
75
  name=name,
60
- description=description,
61
76
  parameters=parameters,
62
- responses=responses,
63
- schema_requests=schema_requests,
64
- summary=summary,
77
+ overloads=overloads,
65
78
  abstract=abstract,
66
79
  want_tracing=want_tracing,
67
80
  )
68
- self.multiple_content_type_parameters = multiple_content_type_parameters
69
- self.api_versions = api_versions
70
- self.multiple_content_type_parameters = multiple_content_type_parameters
71
- self.exceptions = exceptions or []
72
- self.want_description_docstring = want_description_docstring
81
+ self.overloads: List["Operation"] = overloads or []
82
+ self.responses = responses
83
+ self.public = public
73
84
  self.request_builder = request_builder
74
85
  self.deprecated = False
86
+ self.exceptions = exceptions
75
87
 
76
88
  @property
77
- def python_name(self) -> str:
78
- return self.name
79
-
80
- @property
81
- def is_stream_response(self) -> bool:
82
- """Is the response expected to be streamable, like a download."""
83
- return any(response.is_stream_response for response in self.responses)
84
-
85
- @property
86
- def body_kwargs_to_pass_to_request_builder(self) -> List[str]:
87
- return [p.serialized_name for p in self.request_builder.body_kwargs_to_get]
89
+ def operation_type(self) -> str:
90
+ return "operation"
88
91
 
89
92
  @property
90
93
  def has_optional_return_type(self) -> bool:
91
94
  """Has optional return type if there are multiple successful response types where some have
92
95
  bodies and some are None
93
96
  """
97
+ # means if we have at least one successful response with a body and one without
98
+ successful_response_with_body = any(r for r in self.responses if r.type)
99
+ successful_response_without_body = any(r for r in self.responses if not r.type)
100
+ return successful_response_with_body and successful_response_without_body
94
101
 
95
- # successful status codes of responses that have bodies
96
- status_codes_for_responses_with_bodies = [
97
- code
98
- for code in self.success_status_code
99
- if isinstance(code, int) and self.get_response_from_status(code).has_body
100
- ]
101
-
102
- successful_responses = [
103
- response
102
+ def response_type_annotation(self, **kwargs) -> str:
103
+ if (
104
+ self.code_model.options["head_as_boolean"]
105
+ and self.request_builder.method.lower() == "head"
106
+ ):
107
+ return "bool"
108
+ response_type_annotations: OrderedSet[str] = {
109
+ response.type_annotation(**kwargs): None
104
110
  for response in self.responses
105
- if any(code in self.success_status_code for code in response.status_codes)
106
- ]
107
-
108
- return (
109
- self.has_response_body
110
- and len(successful_responses) > 1
111
- and len(self.success_status_code)
112
- != len(status_codes_for_responses_with_bodies)
113
- )
111
+ if response.type
112
+ }
113
+ response_str = ", ".join(response_type_annotations.keys())
114
+ if len(response_type_annotations) > 1:
115
+ return f"Union[{response_str}]"
116
+ if self.has_optional_return_type:
117
+ return f"Optional[{response_str}]"
118
+ if self.responses:
119
+ return self.responses[0].type_annotation(**kwargs)
120
+ return "None"
114
121
 
115
122
  @property
116
- def serialization_context(self) -> str:
117
- # FIXME Do the serialization context (XML)
118
- return ""
123
+ def pylint_disable(self) -> str:
124
+ retval: str = ""
125
+ if self.response_type_annotation(async_mode=False) == "None":
126
+ # doesn't matter if it's async or not
127
+ retval = add_to_pylint_disable(retval, "inconsistent-return-statements")
128
+ return retval
129
+
130
+ def cls_type_annotation(self, *, async_mode: bool) -> str:
131
+ if (
132
+ self.request_builder.method.lower() == "head"
133
+ and self.code_model.options["head_as_boolean"]
134
+ ):
135
+ return "ClsType[None]"
136
+ return f"ClsType[{self.response_type_annotation(async_mode=async_mode)}]"
137
+
138
+ def _response_docstring_helper(self, attr_name: str, **kwargs: Any) -> str:
139
+ responses_with_body = [r for r in self.responses if r.type]
140
+ if (
141
+ self.request_builder.method.lower() == "head"
142
+ and self.code_model.options["head_as_boolean"]
143
+ ):
144
+ return "bool"
145
+ if responses_with_body:
146
+ response_docstring_values: OrderedSet[str] = {
147
+ getattr(response, attr_name)(**kwargs): None
148
+ for response in responses_with_body
149
+ }
150
+ retval = " or ".join(response_docstring_values.keys())
151
+ if self.has_optional_return_type:
152
+ retval += " or None"
153
+ return retval
154
+ if self.responses:
155
+ return getattr(self.responses[0], attr_name)(**kwargs)
156
+ return "None"
157
+
158
+ def response_docstring_text(self, **kwargs) -> str:
159
+ retval = self._response_docstring_helper("docstring_text", **kwargs)
160
+ if not self.code_model.options["version_tolerant"]:
161
+ retval += " or the result of cls(response)"
162
+ return retval
163
+
164
+ def response_docstring_type(self, **kwargs) -> str:
165
+ return self._response_docstring_helper("docstring_type", **kwargs)
119
166
 
120
167
  @property
121
168
  def has_response_body(self) -> bool:
122
169
  """Tell if at least one response has a body."""
123
- return any(
124
- response.has_body or response.is_stream_response
125
- for response in self.responses
126
- )
170
+ return any(response.type for response in self.responses)
127
171
 
128
172
  @property
129
173
  def any_response_has_headers(self) -> bool:
130
- return any(response.has_headers for response in self.responses)
174
+ return any(response.headers for response in self.responses)
131
175
 
132
176
  @property
133
- def default_exception(self) -> Optional[str]:
134
- default_excp = [
135
- excp
136
- for excp in self.exceptions
137
- for code in excp.status_codes
138
- if code == "default"
177
+ def default_error_deserialization(self) -> Optional[str]:
178
+ default_exceptions = [
179
+ e for e in self.exceptions if "default" in e.status_codes and e.type
139
180
  ]
140
- if not default_excp:
181
+ if not default_exceptions:
141
182
  return None
142
- excep_schema = default_excp[0].schema
143
- if isinstance(excep_schema, ObjectSchema):
183
+ excep_schema = default_exceptions[0].type
184
+ if isinstance(excep_schema, ModelType):
144
185
  return f"_models.{excep_schema.name}"
145
- # in this case, it's just an AnySchema
186
+ # in this case, it's just an AnyType
146
187
  return "'object'"
147
188
 
148
189
  @property
149
- def status_code_exceptions(self) -> List[SchemaResponse]:
150
- return [
151
- excp for excp in self.exceptions if list(excp.status_codes) != ["default"]
152
- ]
190
+ def non_default_errors(self) -> List[Response]:
191
+ return [e for e in self.exceptions if "default" not in e.status_codes]
153
192
 
154
193
  @property
155
- def status_code_exceptions_status_codes(self) -> List[Union[str, int]]:
194
+ def non_default_error_status_codes(self) -> List[Union[str, int]]:
156
195
  """Actually returns all of the status codes from exceptions (besides default)"""
157
196
  return list(
158
197
  chain.from_iterable(
159
- [excp.status_codes for excp in self.status_code_exceptions]
198
+ [error.status_codes for error in self.non_default_errors]
160
199
  )
161
200
  )
162
201
 
163
- def _imports_shared(
164
- self, async_mode: bool # pylint: disable=unused-argument
165
- ) -> FileImport:
202
+ def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport:
166
203
  file_import = FileImport()
167
204
  file_import.add_submodule_import(
168
205
  "typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL
169
206
  )
170
207
  for param in self.parameters.method:
171
- if self.abstract and (param.is_multipart or param.is_data_input):
172
- continue
173
- file_import.merge(param.imports())
174
-
175
- for param in self.multiple_content_type_parameters:
176
- file_import.merge(param.imports())
177
-
178
- for response in self.responses:
179
- file_import.merge(response.imports(self.code_model))
180
- if response.has_body:
181
- file_import.merge(cast(BaseSchema, response.schema).imports())
208
+ file_import.merge(param.imports(async_mode, **kwargs))
182
209
 
183
210
  response_types = [
184
- r.type_annotation(is_operation_file=True)
185
- for r in self.responses
186
- if r.has_body
211
+ r.type_annotation(async_mode=async_mode) for r in self.responses if r.type
187
212
  ]
188
213
  if len(set(response_types)) > 1:
189
214
  file_import.add_submodule_import(
190
215
  "typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
191
216
  )
192
-
193
- if self.is_stream_response:
194
- file_import.add_submodule_import(
195
- "typing", "IO", ImportType.STDLIB, TypingSection.CONDITIONAL
196
- )
197
217
  return file_import
198
218
 
199
- def imports_for_multiapi(
200
- self, async_mode: bool
201
- ) -> FileImport: # pylint: disable=unused-argument
202
- return self._imports_shared(async_mode)
203
-
204
- def imports(self, async_mode: bool, is_python3_file: bool) -> FileImport:
205
- file_import = self._imports_base(async_mode, is_python3_file)
219
+ def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
206
220
  if self.abstract:
207
- return file_import
208
- if (
209
- self.has_response_body
210
- and not self.has_optional_return_type
211
- and not self.code_model.options["models_mode"]
212
- ):
213
- file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
221
+ return FileImport()
222
+ file_import = self._imports_shared(async_mode, **kwargs)
223
+ for response in self.responses:
224
+ file_import.merge(
225
+ response.imports_for_multiapi(async_mode=async_mode, **kwargs)
226
+ )
227
+ if self.code_model.options["models_mode"]:
228
+ for exception in self.exceptions:
229
+ file_import.merge(
230
+ exception.imports_for_multiapi(async_mode=async_mode, **kwargs)
231
+ )
214
232
  return file_import
215
233
 
216
234
  @staticmethod
217
235
  def has_kwargs_to_pop_with_default(
218
- kwargs_to_pop: List[Parameter], location: ParameterLocation
236
+ kwargs_to_pop: List[
237
+ Union[
238
+ Parameter,
239
+ RequestBuilderParameter,
240
+ BodyParameter,
241
+ MultipartBodyParameter,
242
+ ]
243
+ ],
244
+ location: ParameterLocation,
219
245
  ) -> bool:
220
246
  return any(
221
- kwarg.has_default_value and kwarg.location == location
247
+ (kwarg.client_default_value or kwarg.optional)
248
+ and kwarg.location == location
222
249
  for kwarg in kwargs_to_pop
223
250
  )
224
251
 
225
- def _imports_base(self, async_mode: bool, is_python3_file: bool) -> FileImport:
226
- file_import = self._imports_shared(async_mode)
227
-
228
- # Exceptions
229
- if self.abstract:
230
- file_import.add_import("abc", ImportType.STDLIB)
231
- else:
232
- file_import.add_submodule_import(
233
- "azure.core.exceptions", "map_error", ImportType.AZURECORE
234
- )
235
- if self.code_model.options["azure_arm"]:
252
+ def get_request_builder_import(
253
+ self,
254
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
255
+ async_mode: bool,
256
+ ) -> FileImport:
257
+ """Helper method to get a request builder import."""
258
+ file_import = FileImport()
259
+ if self.code_model.options["builders_visibility"] != "embedded":
260
+ group_name = request_builder.group_name
261
+ rest_import_path = "..." if async_mode else ".."
262
+ if group_name:
236
263
  file_import.add_submodule_import(
237
- "azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.AZURECORE
264
+ f"{rest_import_path}{self.code_model.rest_layer_name}",
265
+ group_name,
266
+ import_type=ImportType.LOCAL,
267
+ alias=f"rest_{group_name}",
238
268
  )
239
- file_import.add_submodule_import(
240
- "azure.core.exceptions", "HttpResponseError", ImportType.AZURECORE
269
+ else:
270
+ file_import.add_submodule_import(
271
+ rest_import_path,
272
+ self.code_model.rest_layer_name,
273
+ import_type=ImportType.LOCAL,
274
+ alias="rest",
275
+ )
276
+ if self.code_model.options["builders_visibility"] == "embedded" and async_mode:
277
+ suffix = (
278
+ "_py3"
279
+ if self.code_model.options["add_python3_operation_files"]
280
+ and not self.code_model.options["python3_only"]
281
+ else ""
241
282
  )
242
283
  file_import.add_submodule_import(
243
- "azure.core.exceptions",
244
- "ClientAuthenticationError",
245
- ImportType.AZURECORE,
284
+ f"...{self.code_model.operations_folder_name}.{self.filename}{suffix}",
285
+ request_builder.name,
286
+ import_type=ImportType.LOCAL,
246
287
  )
288
+ return file_import
289
+
290
+ def imports(
291
+ self, async_mode: bool, is_python3_file: bool, **kwargs: Any
292
+ ) -> FileImport:
293
+ if self.abstract:
294
+ return FileImport()
295
+ file_import = self._imports_shared(async_mode, **kwargs)
296
+
297
+ for response in self.responses:
298
+ file_import.merge(response.imports(async_mode=async_mode, **kwargs))
299
+ if self.code_model.options["models_mode"]:
300
+ for exception in self.exceptions:
301
+ file_import.merge(exception.imports(async_mode=async_mode, **kwargs))
302
+
303
+ if self.parameters.has_body and self.parameters.body_parameter.flattened:
304
+ file_import.merge(self.parameters.body_parameter.type.imports(**kwargs))
305
+
306
+ # Exceptions
307
+ file_import.add_submodule_import(
308
+ "azure.core.exceptions", "map_error", ImportType.AZURECORE
309
+ )
310
+ if self.code_model.options["azure_arm"]:
247
311
  file_import.add_submodule_import(
248
- "azure.core.exceptions", "ResourceNotFoundError", ImportType.AZURECORE
312
+ "azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.AZURECORE
249
313
  )
314
+ file_import.add_submodule_import(
315
+ "azure.core.exceptions", "HttpResponseError", ImportType.AZURECORE
316
+ )
317
+ file_import.add_submodule_import(
318
+ "azure.core.exceptions",
319
+ "ClientAuthenticationError",
320
+ ImportType.AZURECORE,
321
+ )
322
+ file_import.add_submodule_import(
323
+ "azure.core.exceptions", "ResourceNotFoundError", ImportType.AZURECORE
324
+ )
325
+ file_import.add_submodule_import(
326
+ "azure.core.exceptions", "ResourceExistsError", ImportType.AZURECORE
327
+ )
328
+
329
+ kwargs_to_pop = self.parameters.kwargs_to_pop(is_python3_file)
330
+ if self.has_kwargs_to_pop_with_default(
331
+ kwargs_to_pop, ParameterLocation.HEADER
332
+ ) or self.has_kwargs_to_pop_with_default(
333
+ kwargs_to_pop, ParameterLocation.QUERY
334
+ ):
250
335
  file_import.add_submodule_import(
251
- "azure.core.exceptions", "ResourceExistsError", ImportType.AZURECORE
336
+ "azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE
252
337
  )
338
+ if self.deprecated:
339
+ file_import.add_import("warnings", ImportType.STDLIB)
253
340
 
254
- kwargs_to_pop = self.parameters.kwargs_to_pop(is_python3_file)
255
- if self.has_kwargs_to_pop_with_default(
256
- kwargs_to_pop, ParameterLocation.Header
257
- ) or self.has_kwargs_to_pop_with_default(
258
- kwargs_to_pop, ParameterLocation.Query
259
- ):
260
- file_import.add_submodule_import(
261
- "azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE
262
- )
263
- if self.deprecated:
264
- file_import.add_import("warnings", ImportType.STDLIB)
265
- if self.code_model.options["builders_visibility"] != "embedded":
266
- builder_group_name = self.request_builder.builder_group_name
267
- rest_import_path = "..." if async_mode else ".."
268
- if builder_group_name:
269
- file_import.add_submodule_import(
270
- f"{rest_import_path}{self.code_model.rest_layer_name}",
271
- builder_group_name,
272
- import_type=ImportType.LOCAL,
273
- alias=f"rest_{builder_group_name}",
274
- )
275
- else:
276
- file_import.add_submodule_import(
277
- rest_import_path,
278
- self.code_model.rest_layer_name,
279
- import_type=ImportType.LOCAL,
280
- alias="rest",
281
- )
282
- if self.code_model.need_request_converter:
283
- relative_path = "..." if async_mode else ".."
284
- file_import.add_submodule_import(
285
- f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
286
- )
341
+ if self.code_model.need_request_converter:
342
+ relative_path = "..." if async_mode else ".."
343
+ file_import.add_submodule_import(
344
+ f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
345
+ )
287
346
  if async_mode:
288
347
  file_import.add_submodule_import(
289
348
  "azure.core.pipeline.transport",
@@ -317,115 +376,73 @@ class Operation(
317
376
  file_import.add_submodule_import(
318
377
  "typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL
319
378
  )
320
- if self.code_model.options["tracing"] and self.want_tracing:
379
+ if self.code_model.options["tracing"] and self.want_tracing and not async_mode:
321
380
  file_import.add_submodule_import(
322
- f"azure.core.tracing.decorator{'_async' if async_mode else ''}",
323
- f"distributed_trace{'_async' if async_mode else ''}",
381
+ f"azure.core.tracing.decorator",
382
+ f"distributed_trace",
324
383
  ImportType.AZURECORE,
325
384
  )
326
-
385
+ file_import.merge(
386
+ self.get_request_builder_import(self.request_builder, async_mode)
387
+ )
388
+ if self.overloads:
389
+ file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
327
390
  return file_import
328
391
 
329
- def _get_body_param_from_body_kwarg(self, body_kwarg: Parameter) -> Parameter:
330
- # determine which of the body parameters returned from m4 corresponds to this body_kwarg
331
- if not self.multiple_content_type_parameters.has_body:
332
- return self.parameters.body[0]
333
- if body_kwarg.serialized_name == "json":
334
- # first check if there's any non-binary. In the case of multiple content types, there's
335
- # usually one binary (for content), and one schema parameter (for json)
336
- try:
337
- return next(
338
- p
339
- for p in self.multiple_content_type_parameters.body
340
- if not isinstance(p.schema, IOSchema)
341
- )
342
- except StopIteration:
343
- return next(
344
- p
345
- for p in self.multiple_content_type_parameters.body
346
- if p.is_json_parameter
347
- )
348
- return self.multiple_content_type_parameters.body[0]
349
-
350
- def link_body_kwargs_to_body_params(self) -> None:
351
- if not self.parameters.has_body:
352
- return
353
- body_kwargs = [
354
- p for p in self.request_builder.parameters.body if p.content_types
355
- ]
356
- if len(body_kwargs) == 1:
357
- self.parameters.body[0].body_kwargs = [body_kwargs[0]]
358
- return
359
- for body_kwarg in body_kwargs:
360
- body_param = self._get_body_param_from_body_kwarg(body_kwarg)
361
- body_param.body_kwargs.append(body_kwarg)
362
-
363
- def convert_multiple_content_type_parameters(self) -> None:
364
- type_annot = ", ".join(
365
- [
366
- param.schema.type_annotation(is_operation_file=True)
367
- for param in self.multiple_content_type_parameters
368
- ]
369
- )
370
- docstring_type = " or ".join(
371
- [
372
- param.schema.docstring_type
373
- for param in self.multiple_content_type_parameters
374
- ]
375
- )
392
+ def get_response_from_status(
393
+ self, status_code: Optional[Union[str, int]]
394
+ ) -> ResponseType:
376
395
  try:
377
- # get an optional param with object first. These params are the top choice
378
- # bc they have more info about how to serialize the body
379
- chosen_parameter = next(
380
- p
381
- for p in self.multiple_content_type_parameters
382
- if not p.required and isinstance(p.schema, ObjectSchema)
383
- )
384
- except StopIteration: # pylint: disable=broad-except
385
- # otherwise, we get the first optional param, if that exists. If not, we just grab the first one
386
- optional_parameters = [
387
- p for p in self.multiple_content_type_parameters if not p.required
388
- ]
389
- chosen_parameter = (
390
- optional_parameters[0]
391
- if optional_parameters
392
- else self.multiple_content_type_parameters[0]
393
- )
394
- if not chosen_parameter:
396
+ return next(r for r in self.responses if status_code in r.status_codes)
397
+ except StopIteration:
395
398
  raise ValueError(
396
- "You are missing a parameter that has multiple media types"
399
+ f"Incorrect status code {status_code}, operation {self.name}"
397
400
  )
398
- chosen_parameter.multiple_content_types_type_annot = f"Union[{type_annot}]"
399
- chosen_parameter.multiple_content_types_docstring_type = docstring_type
400
- self.parameters.append(chosen_parameter)
401
+
402
+ @property
403
+ def success_status_codes(self) -> List[Union[str, int]]:
404
+ """The list of all successfull status code."""
405
+ return [code for response in self.responses for code in response.status_codes]
406
+
407
+ @property
408
+ def filename(self) -> str:
409
+ basename = self.group_name
410
+ if basename == "":
411
+ # in a mixin
412
+ basename = self.code_model.module_name
413
+
414
+ if (
415
+ basename == "operations"
416
+ or self.code_model.options["combine_operation_files"]
417
+ ):
418
+ return f"_operations"
419
+ return f"_{basename}_operations"
420
+
421
+ @property
422
+ def has_stream_response(self) -> bool:
423
+ return any(r.is_stream_response for r in self.responses)
401
424
 
402
425
  @classmethod
403
- def from_yaml(
404
- cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
405
- ) -> "Operation":
406
- name = yaml_data["language"]["python"]["name"]
407
- _LOGGER.debug("Parsing %s operation", name)
408
-
409
- parameter_creator = get_parameter(code_model).from_yaml
410
- schema_requests = [
411
- SchemaRequest.from_yaml(yaml, code_model=code_model)
412
- for yaml in yaml_data["requests"]
426
+ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):
427
+ name = yaml_data["name"]
428
+ request_builder = code_model.lookup_request_builder(id(yaml_data))
429
+ responses = [
430
+ cast(ResponseType, get_response(r, code_model))
431
+ for r in yaml_data["responses"]
432
+ ]
433
+ exceptions = [
434
+ Response.from_yaml(e, code_model) for e in yaml_data["exceptions"]
435
+ ]
436
+ parameter_list = ParameterList.from_yaml(yaml_data, code_model)
437
+ overloads = [
438
+ cls.from_yaml(overload, code_model)
439
+ for overload in yaml_data.get("overloads", [])
413
440
  ]
414
- parameters, multiple_content_type_parameters = create_parameters(
415
- yaml_data, code_model, parameter_creator
416
- )
417
- parameter_list = ParameterList(code_model, parameters, schema_requests)
418
- multiple_content_type_parameter_list = ParameterList(
419
- code_model, multiple_content_type_parameters, schema_requests
420
- )
421
441
  abstract = False
422
- if code_model.options["version_tolerant"] and (
423
- any(p for p in parameter_list if p.is_multipart or p.is_data_input)
424
- or any(
425
- p
426
- for p in multiple_content_type_parameter_list
427
- if p.is_multipart or p.is_data_input
428
- )
442
+ if (
443
+ code_model.options["version_tolerant"]
444
+ and parameter_list.has_body
445
+ and isinstance(parameter_list.body_parameter, MultipartBodyParameter)
429
446
  ):
430
447
  _LOGGER.warning(
431
448
  'Not going to generate operation "%s" because it has multipart / urlencoded body parameters. '
@@ -436,34 +453,50 @@ class Operation(
436
453
  )
437
454
  abstract = True
438
455
 
439
- if len(parameter_list.content_types) > 1:
440
- for p in parameter_list.parameters:
441
- if p.rest_api_name == "Content-Type":
442
- p.method_location = ParameterMethodLocation.KEYWORD_ONLY
443
- request_builder = code_model.lookup_request_builder(id(yaml_data))
444
-
445
456
  return cls(
446
- code_model=code_model,
447
457
  yaml_data=yaml_data,
458
+ code_model=code_model,
448
459
  request_builder=request_builder,
449
460
  name=name,
450
- description=yaml_data["language"]["python"]["description"],
451
- api_versions=set(
452
- value_dict["version"] for value_dict in yaml_data["apiVersions"]
453
- ),
454
461
  parameters=parameter_list,
455
- multiple_content_type_parameters=multiple_content_type_parameter_list,
456
- schema_requests=schema_requests,
457
- summary=yaml_data["language"]["python"].get("summary"),
458
- responses=[
459
- SchemaResponse.from_yaml(yaml, code_model=code_model)
460
- for yaml in yaml_data.get("responses", [])
461
- ],
462
- # Exception with no schema means default exception, we don't store them
463
- exceptions=[
464
- SchemaResponse.from_yaml(yaml, code_model=code_model)
465
- for yaml in yaml_data.get("exceptions", [])
466
- if "schema" in yaml
467
- ],
462
+ overloads=overloads,
463
+ responses=responses,
464
+ exceptions=exceptions,
465
+ want_tracing=not yaml_data["isOverload"],
468
466
  abstract=abstract,
469
467
  )
468
+
469
+
470
+ class Operation(OperationBase[Response]):
471
+ def imports(
472
+ self, async_mode: bool, is_python3_file: bool, **kwargs: Any
473
+ ) -> FileImport:
474
+ file_import = super().imports(async_mode, is_python3_file, **kwargs)
475
+ if self.abstract:
476
+ return file_import
477
+ if async_mode:
478
+ file_import.add_submodule_import(
479
+ f"azure.core.tracing.decorator_async",
480
+ f"distributed_trace_async",
481
+ ImportType.AZURECORE,
482
+ )
483
+ if (
484
+ self.has_response_body
485
+ and not self.has_optional_return_type
486
+ and not self.code_model.options["models_mode"]
487
+ ):
488
+ file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
489
+
490
+ return file_import
491
+
492
+
493
+ def get_operation(yaml_data: Dict[str, Any], code_model: "CodeModel") -> OperationBase:
494
+ if yaml_data["discriminator"] == "lropaging":
495
+ from .lro_paging_operation import LROPagingOperation as OperationCls
496
+ elif yaml_data["discriminator"] == "lro":
497
+ from .lro_operation import LROOperation as OperationCls # type: ignore
498
+ elif yaml_data["discriminator"] == "paging":
499
+ from .paging_operation import PagingOperation as OperationCls # type: ignore
500
+ else:
501
+ from . import Operation as OperationCls # type: ignore
502
+ return OperationCls.from_yaml(yaml_data, code_model)