@autorest/python 5.16.0 → 5.17.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 (92) hide show
  1. package/ChangeLog.md +43 -3
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +1 -1
  4. package/autorest/codegen/__init__.py +48 -209
  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} +56 -40
  9. package/autorest/codegen/models/client.py +157 -48
  10. package/autorest/codegen/models/code_model.py +108 -254
  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_schema.py → dictionary_type.py} +41 -31
  15. package/autorest/codegen/models/enum_type.py +195 -0
  16. package/autorest/codegen/models/imports.py +23 -0
  17. package/autorest/codegen/models/list_type.py +134 -0
  18. package/autorest/codegen/models/lro_operation.py +77 -156
  19. package/autorest/codegen/models/lro_paging_operation.py +28 -11
  20. package/autorest/codegen/models/model_type.py +239 -0
  21. package/autorest/codegen/models/operation.py +303 -269
  22. package/autorest/codegen/models/operation_group.py +48 -89
  23. package/autorest/codegen/models/paging_operation.py +80 -123
  24. package/autorest/codegen/models/parameter.py +289 -396
  25. package/autorest/codegen/models/parameter_list.py +348 -360
  26. package/autorest/codegen/models/primitive_types.py +544 -0
  27. package/autorest/codegen/models/property.py +109 -139
  28. package/autorest/codegen/models/request_builder.py +105 -88
  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 +46 -37
  33. package/autorest/codegen/serializers/builder_serializer.py +604 -1146
  34. package/autorest/codegen/serializers/client_serializer.py +83 -88
  35. package/autorest/codegen/serializers/general_serializer.py +5 -64
  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 +40 -32
  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 +4 -13
  43. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  44. package/autorest/codegen/serializers/request_builders_serializer.py +12 -29
  45. package/autorest/codegen/serializers/utils.py +0 -142
  46. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  47. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -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 +7 -7
  60. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
  61. package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
  62. package/autorest/codegen/templates/paging_operation.py.jinja2 +2 -2
  63. package/autorest/codegen/templates/request_builder.py.jinja2 +13 -11
  64. package/autorest/codegen/templates/setup.py.jinja2 +9 -3
  65. package/autorest/codegen/templates/vendor.py.jinja2 +1 -1
  66. package/autorest/jsonrpc/server.py +15 -3
  67. package/autorest/m4reformatter/__init__.py +1108 -0
  68. package/autorest/multiapi/models/code_model.py +1 -1
  69. package/autorest/multiapi/serializers/__init__.py +4 -4
  70. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  71. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  72. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +3 -3
  73. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  74. package/autorest/postprocess/__init__.py +202 -0
  75. package/autorest/postprocess/get_all.py +19 -0
  76. package/autorest/postprocess/venvtools.py +73 -0
  77. package/autorest/preprocess/__init__.py +209 -0
  78. package/autorest/preprocess/helpers.py +54 -0
  79. package/autorest/{namer → preprocess}/python_mappings.py +21 -16
  80. package/package.json +2 -2
  81. package/autorest/codegen/models/credential_model.py +0 -55
  82. package/autorest/codegen/models/credential_schema.py +0 -95
  83. package/autorest/codegen/models/credential_schema_policy.py +0 -73
  84. package/autorest/codegen/models/enum_schema.py +0 -225
  85. package/autorest/codegen/models/list_schema.py +0 -135
  86. package/autorest/codegen/models/object_schema.py +0 -303
  87. package/autorest/codegen/models/primitive_schemas.py +0 -495
  88. package/autorest/codegen/models/request_builder_parameter_list.py +0 -249
  89. package/autorest/codegen/models/schema_request.py +0 -55
  90. package/autorest/codegen/models/schema_response.py +0 -141
  91. package/autorest/namer/__init__.py +0 -23
  92. package/autorest/namer/name_converter.py +0 -509
@@ -3,227 +3,302 @@
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
9
21
 
10
- from .base_builder import BaseBuilder, create_parameters
22
+ from .utils import OrderedSet, add_to_pylint_disable
23
+
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
- 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())
207
+ if not self.abstract:
208
+ for param in self.parameters.method:
209
+ file_import.merge(param.imports(async_mode, **kwargs))
182
210
 
183
211
  response_types = [
184
- r.type_annotation(is_operation_file=True)
185
- for r in self.responses
186
- if r.has_body
212
+ r.type_annotation(async_mode=async_mode) for r in self.responses if r.type
187
213
  ]
188
214
  if len(set(response_types)) > 1:
189
215
  file_import.add_submodule_import(
190
216
  "typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
191
217
  )
192
-
193
- if self.is_stream_response:
194
- file_import.add_submodule_import(
195
- "typing", "IO", ImportType.STDLIB, TypingSection.CONDITIONAL
196
- )
197
218
  return file_import
198
219
 
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)
206
- 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)
220
+ def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
221
+ file_import = self._imports_shared(async_mode, **kwargs)
222
+ for response in self.responses:
223
+ file_import.merge(
224
+ response.imports_for_multiapi(async_mode=async_mode, **kwargs)
225
+ )
226
+ if self.code_model.options["models_mode"]:
227
+ for exception in self.exceptions:
228
+ file_import.merge(
229
+ exception.imports_for_multiapi(async_mode=async_mode, **kwargs)
230
+ )
214
231
  return file_import
215
232
 
216
233
  @staticmethod
217
234
  def has_kwargs_to_pop_with_default(
218
- kwargs_to_pop: List[Parameter], location: ParameterLocation
235
+ kwargs_to_pop: List[
236
+ Union[
237
+ Parameter,
238
+ RequestBuilderParameter,
239
+ BodyParameter,
240
+ MultipartBodyParameter,
241
+ ]
242
+ ],
243
+ location: ParameterLocation,
219
244
  ) -> bool:
220
245
  return any(
221
- kwarg.has_default_value and kwarg.location == location
246
+ (kwarg.client_default_value or kwarg.optional)
247
+ and kwarg.location == location
222
248
  for kwarg in kwargs_to_pop
223
249
  )
224
250
 
225
- def _imports_base(self, async_mode: bool, is_python3_file: bool) -> FileImport:
226
- file_import = self._imports_shared(async_mode)
251
+ def get_request_builder_import(
252
+ self,
253
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
254
+ async_mode: bool,
255
+ ) -> FileImport:
256
+ """Helper method to get a request builder import."""
257
+ file_import = FileImport()
258
+ if self.code_model.options["builders_visibility"] != "embedded":
259
+ group_name = request_builder.group_name
260
+ rest_import_path = "..." if async_mode else ".."
261
+ if group_name:
262
+ file_import.add_submodule_import(
263
+ f"{rest_import_path}{self.code_model.rest_layer_name}",
264
+ group_name,
265
+ import_type=ImportType.LOCAL,
266
+ alias=f"rest_{group_name}",
267
+ )
268
+ else:
269
+ file_import.add_submodule_import(
270
+ rest_import_path,
271
+ self.code_model.rest_layer_name,
272
+ import_type=ImportType.LOCAL,
273
+ alias="rest",
274
+ )
275
+ if self.code_model.options["builders_visibility"] == "embedded" and async_mode:
276
+ suffix = (
277
+ "_py3"
278
+ if self.code_model.options["add_python3_operation_files"]
279
+ and not self.code_model.options["python3_only"]
280
+ else ""
281
+ )
282
+ file_import.add_submodule_import(
283
+ f"...{self.code_model.operations_folder_name}.{self.filename}{suffix}",
284
+ request_builder.name,
285
+ import_type=ImportType.LOCAL,
286
+ )
287
+ return file_import
288
+
289
+ def imports(
290
+ self, async_mode: bool, is_python3_file: bool, **kwargs: Any
291
+ ) -> FileImport:
292
+ file_import = self._imports_shared(async_mode, **kwargs)
293
+
294
+ for response in self.responses:
295
+ file_import.merge(response.imports(async_mode=async_mode, **kwargs))
296
+ if self.code_model.options["models_mode"]:
297
+ for exception in self.exceptions:
298
+ file_import.merge(exception.imports(async_mode=async_mode, **kwargs))
299
+
300
+ if self.parameters.has_body and self.parameters.body_parameter.flattened:
301
+ file_import.merge(self.parameters.body_parameter.type.imports(**kwargs))
227
302
 
228
303
  # Exceptions
229
304
  if self.abstract:
@@ -253,32 +328,16 @@ class Operation(
253
328
 
254
329
  kwargs_to_pop = self.parameters.kwargs_to_pop(is_python3_file)
255
330
  if self.has_kwargs_to_pop_with_default(
256
- kwargs_to_pop, ParameterLocation.Header
331
+ kwargs_to_pop, ParameterLocation.HEADER
257
332
  ) or self.has_kwargs_to_pop_with_default(
258
- kwargs_to_pop, ParameterLocation.Query
333
+ kwargs_to_pop, ParameterLocation.QUERY
259
334
  ):
260
335
  file_import.add_submodule_import(
261
336
  "azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE
262
337
  )
263
338
  if self.deprecated:
264
339
  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
- )
340
+
282
341
  if self.code_model.need_request_converter:
283
342
  relative_path = "..." if async_mode else ".."
284
343
  file_import.add_submodule_import(
@@ -317,115 +376,74 @@ 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
+ if not self.abstract:
386
+ file_import.merge(
387
+ self.get_request_builder_import(self.request_builder, async_mode)
388
+ )
389
+ if self.overloads:
390
+ file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
327
391
  return file_import
328
392
 
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
- )
393
+ def get_response_from_status(
394
+ self, status_code: Optional[Union[str, int]]
395
+ ) -> ResponseType:
376
396
  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:
397
+ return next(r for r in self.responses if status_code in r.status_codes)
398
+ except StopIteration:
395
399
  raise ValueError(
396
- "You are missing a parameter that has multiple media types"
400
+ f"Incorrect status code {status_code}, operation {self.name}"
397
401
  )
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)
402
+
403
+ @property
404
+ def success_status_codes(self) -> List[Union[str, int]]:
405
+ """The list of all successfull status code."""
406
+ return [code for response in self.responses for code in response.status_codes]
407
+
408
+ @property
409
+ def filename(self) -> str:
410
+ basename = self.group_name
411
+ if basename == "":
412
+ # in a mixin
413
+ basename = self.code_model.module_name
414
+
415
+ if (
416
+ basename == "operations"
417
+ or self.code_model.options["combine_operation_files"]
418
+ ):
419
+ return f"_operations"
420
+ return f"_{basename}_operations"
421
+
422
+ @property
423
+ def has_stream_response(self) -> bool:
424
+ return any(r.is_stream_response for r in self.responses)
401
425
 
402
426
  @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"]
427
+ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):
428
+ name = yaml_data["name"]
429
+ request_builder = code_model.lookup_request_builder(id(yaml_data))
430
+ responses = [
431
+ cast(ResponseType, get_response(r, code_model))
432
+ for r in yaml_data["responses"]
433
+ ]
434
+ exceptions = [
435
+ Response.from_yaml(e, code_model) for e in yaml_data["exceptions"]
436
+ ]
437
+ parameter_list = ParameterList.from_yaml(yaml_data, code_model)
438
+ overloads = [
439
+ cls.from_yaml(overload, code_model)
440
+ for overload in yaml_data.get("overloads", [])
413
441
  ]
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
442
  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
- )
443
+ if (
444
+ code_model.options["version_tolerant"]
445
+ and parameter_list.has_body
446
+ and isinstance(parameter_list.body_parameter, MultipartBodyParameter)
429
447
  ):
430
448
  _LOGGER.warning(
431
449
  'Not going to generate operation "%s" because it has multipart / urlencoded body parameters. '
@@ -436,34 +454,50 @@ class Operation(
436
454
  )
437
455
  abstract = True
438
456
 
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
457
  return cls(
446
- code_model=code_model,
447
458
  yaml_data=yaml_data,
459
+ code_model=code_model,
448
460
  request_builder=request_builder,
449
461
  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
462
  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
- ],
463
+ overloads=overloads,
464
+ responses=responses,
465
+ exceptions=exceptions,
466
+ want_tracing=not yaml_data["isOverload"],
468
467
  abstract=abstract,
469
468
  )
469
+
470
+
471
+ class Operation(OperationBase[Response]):
472
+ def imports(
473
+ self, async_mode: bool, is_python3_file: bool, **kwargs: Any
474
+ ) -> FileImport:
475
+ file_import = super().imports(async_mode, is_python3_file, **kwargs)
476
+ if async_mode:
477
+ file_import.add_submodule_import(
478
+ f"azure.core.tracing.decorator_async",
479
+ f"distributed_trace_async",
480
+ ImportType.AZURECORE,
481
+ )
482
+ if self.abstract:
483
+ return file_import
484
+ if (
485
+ self.has_response_body
486
+ and not self.has_optional_return_type
487
+ and not self.code_model.options["models_mode"]
488
+ ):
489
+ file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
490
+
491
+ return file_import
492
+
493
+
494
+ def get_operation(yaml_data: Dict[str, Any], code_model: "CodeModel") -> OperationBase:
495
+ if yaml_data["discriminator"] == "lropaging":
496
+ from .lro_paging_operation import LROPagingOperation as OperationCls
497
+ elif yaml_data["discriminator"] == "lro":
498
+ from .lro_operation import LROOperation as OperationCls # type: ignore
499
+ elif yaml_data["discriminator"] == "paging":
500
+ from .paging_operation import PagingOperation as OperationCls # type: ignore
501
+ else:
502
+ from . import Operation as OperationCls # type: ignore
503
+ return OperationCls.from_yaml(yaml_data, code_model)