@autorest/python 5.14.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 (120) hide show
  1. package/ChangeLog.md +91 -2
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +2 -3
  4. package/autorest/black/__init__.py +12 -5
  5. package/autorest/codegen/__init__.py +130 -179
  6. package/autorest/codegen/models/__init__.py +122 -78
  7. package/autorest/codegen/models/base_builder.py +70 -72
  8. package/autorest/codegen/models/base_model.py +7 -5
  9. package/autorest/codegen/models/{base_schema.py → base_type.py} +62 -49
  10. package/autorest/codegen/models/client.py +195 -36
  11. package/autorest/codegen/models/code_model.py +165 -299
  12. package/autorest/codegen/models/combined_type.py +107 -0
  13. package/autorest/codegen/models/constant_type.py +122 -0
  14. package/autorest/codegen/models/credential_types.py +224 -0
  15. package/autorest/codegen/models/dictionary_type.py +116 -0
  16. package/autorest/codegen/models/enum_type.py +195 -0
  17. package/autorest/codegen/models/imports.py +95 -41
  18. package/autorest/codegen/models/list_type.py +134 -0
  19. package/autorest/codegen/models/lro_operation.py +90 -133
  20. package/autorest/codegen/models/lro_paging_operation.py +28 -12
  21. package/autorest/codegen/models/model_type.py +239 -0
  22. package/autorest/codegen/models/operation.py +415 -241
  23. package/autorest/codegen/models/operation_group.py +82 -88
  24. package/autorest/codegen/models/paging_operation.py +101 -117
  25. package/autorest/codegen/models/parameter.py +307 -322
  26. package/autorest/codegen/models/parameter_list.py +366 -357
  27. package/autorest/codegen/models/primitive_types.py +544 -0
  28. package/autorest/codegen/models/property.py +122 -134
  29. package/autorest/codegen/models/request_builder.py +138 -86
  30. package/autorest/codegen/models/request_builder_parameter.py +122 -79
  31. package/autorest/codegen/models/response.py +325 -0
  32. package/autorest/codegen/models/utils.py +17 -1
  33. package/autorest/codegen/serializers/__init__.py +242 -118
  34. package/autorest/codegen/serializers/builder_serializer.py +863 -1027
  35. package/autorest/codegen/serializers/client_serializer.py +148 -82
  36. package/autorest/codegen/serializers/general_serializer.py +44 -47
  37. package/autorest/codegen/serializers/import_serializer.py +96 -31
  38. package/autorest/codegen/serializers/metadata_serializer.py +39 -79
  39. package/autorest/codegen/serializers/model_base_serializer.py +65 -29
  40. package/autorest/codegen/serializers/model_generic_serializer.py +9 -10
  41. package/autorest/codegen/serializers/model_init_serializer.py +4 -2
  42. package/autorest/codegen/serializers/model_python3_serializer.py +29 -22
  43. package/autorest/codegen/serializers/operation_groups_serializer.py +21 -18
  44. package/autorest/codegen/serializers/operations_init_serializer.py +23 -11
  45. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  46. package/autorest/codegen/serializers/patch_serializer.py +14 -2
  47. package/autorest/codegen/serializers/request_builders_serializer.py +57 -0
  48. package/autorest/codegen/serializers/utils.py +0 -103
  49. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  50. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -7
  51. package/autorest/codegen/templates/config.py.jinja2 +13 -13
  52. package/autorest/codegen/templates/enum.py.jinja2 +4 -4
  53. package/autorest/codegen/templates/enum_container.py.jinja2 +1 -2
  54. package/autorest/codegen/templates/init.py.jinja2 +9 -6
  55. package/autorest/codegen/templates/keywords.jinja2 +14 -1
  56. package/autorest/codegen/templates/lro_operation.py.jinja2 +6 -5
  57. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +6 -5
  58. package/autorest/codegen/templates/metadata.json.jinja2 +36 -35
  59. package/autorest/codegen/templates/model.py.jinja2 +23 -29
  60. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  61. package/autorest/codegen/templates/model_init.py.jinja2 +9 -8
  62. package/autorest/codegen/templates/operation.py.jinja2 +10 -15
  63. package/autorest/codegen/templates/operation_group.py.jinja2 +14 -13
  64. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -2
  65. package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
  66. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
  67. package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
  68. package/autorest/codegen/templates/patch.py.jinja2 +18 -29
  69. package/autorest/codegen/templates/request_builder.py.jinja2 +20 -13
  70. package/autorest/codegen/templates/setup.py.jinja2 +9 -3
  71. package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
  72. package/autorest/jsonrpc/__init__.py +7 -12
  73. package/autorest/jsonrpc/localapi.py +4 -3
  74. package/autorest/jsonrpc/server.py +28 -9
  75. package/autorest/jsonrpc/stdstream.py +13 -6
  76. package/autorest/m2r/__init__.py +5 -8
  77. package/autorest/m4reformatter/__init__.py +1108 -0
  78. package/autorest/multiapi/__init__.py +24 -14
  79. package/autorest/multiapi/models/client.py +21 -11
  80. package/autorest/multiapi/models/code_model.py +23 -10
  81. package/autorest/multiapi/models/config.py +4 -1
  82. package/autorest/multiapi/models/constant_global_parameter.py +1 -0
  83. package/autorest/multiapi/models/global_parameter.py +2 -1
  84. package/autorest/multiapi/models/global_parameters.py +14 -8
  85. package/autorest/multiapi/models/imports.py +35 -18
  86. package/autorest/multiapi/models/mixin_operation.py +5 -5
  87. package/autorest/multiapi/models/operation_group.py +2 -1
  88. package/autorest/multiapi/models/operation_mixin_group.py +21 -10
  89. package/autorest/multiapi/serializers/__init__.py +20 -25
  90. package/autorest/multiapi/serializers/import_serializer.py +47 -15
  91. package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
  92. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  93. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  94. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  95. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  96. package/autorest/multiapi/utils.py +3 -3
  97. package/autorest/postprocess/__init__.py +202 -0
  98. package/autorest/postprocess/get_all.py +19 -0
  99. package/autorest/postprocess/venvtools.py +73 -0
  100. package/autorest/preprocess/__init__.py +209 -0
  101. package/autorest/preprocess/helpers.py +54 -0
  102. package/autorest/{namer → preprocess}/python_mappings.py +25 -32
  103. package/package.json +3 -3
  104. package/run-python3.js +2 -3
  105. package/venvtools.py +1 -1
  106. package/autorest/codegen/models/constant_schema.py +0 -97
  107. package/autorest/codegen/models/credential_schema.py +0 -90
  108. package/autorest/codegen/models/credential_schema_policy.py +0 -77
  109. package/autorest/codegen/models/dictionary_schema.py +0 -103
  110. package/autorest/codegen/models/enum_schema.py +0 -246
  111. package/autorest/codegen/models/list_schema.py +0 -113
  112. package/autorest/codegen/models/object_schema.py +0 -249
  113. package/autorest/codegen/models/primitive_schemas.py +0 -476
  114. package/autorest/codegen/models/request_builder_parameter_list.py +0 -280
  115. package/autorest/codegen/models/rest.py +0 -42
  116. package/autorest/codegen/models/schema_request.py +0 -45
  117. package/autorest/codegen/models/schema_response.py +0 -123
  118. package/autorest/codegen/serializers/rest_serializer.py +0 -57
  119. package/autorest/namer/__init__.py +0 -25
  120. package/autorest/namer/name_converter.py +0 -412
@@ -3,327 +3,501 @@
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
9
-
10
- from .base_builder import BaseBuilder, create_parameters
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
23
+
24
+ from .base_builder import BaseBuilder
11
25
  from .imports import FileImport, ImportType, TypingSection
12
- from .schema_response import SchemaResponse
13
- from .parameter import Parameter, get_parameter
14
- from .parameter_list import ParameterList, get_parameter_list
15
- from .base_schema import BaseSchema
16
- from .object_schema import ObjectSchema
17
- from .request_builder import RequestBuilder
18
- from .schema_request import SchemaRequest
19
- from .primitive_schemas import IOSchema
26
+ from .response import (
27
+ Response,
28
+ PagingResponse,
29
+ LROResponse,
30
+ LROPagingResponse,
31
+ get_response,
32
+ )
33
+ from .parameter import (
34
+ BodyParameter,
35
+ MultipartBodyParameter,
36
+ Parameter,
37
+ ParameterLocation,
38
+ )
39
+ from .parameter_list import ParameterList
40
+ from .model_type import ModelType
41
+ from .request_builder import OverloadedRequestBuilder, RequestBuilder
42
+
43
+ if TYPE_CHECKING:
44
+ from .code_model import CodeModel
20
45
 
21
46
  _LOGGER = logging.getLogger(__name__)
22
47
 
23
- class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-many-instance-attributes
24
- """Represent an self.
25
- """
48
+ ResponseType = TypeVar(
49
+ "ResponseType",
50
+ bound=Union[Response, PagingResponse, LROResponse, LROPagingResponse],
51
+ )
52
+
26
53
 
54
+ class OperationBase( # pylint: disable=too-many-public-methods
55
+ Generic[ResponseType], BaseBuilder[ParameterList]
56
+ ):
27
57
  def __init__(
28
58
  self,
29
- code_model,
30
59
  yaml_data: Dict[str, Any],
60
+ code_model: "CodeModel",
31
61
  name: str,
32
- description: str,
33
- api_versions: Set[str],
62
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
34
63
  parameters: ParameterList,
35
- multiple_content_type_parameters: ParameterList,
36
- schema_requests: List[SchemaRequest],
37
- summary: Optional[str] = None,
38
- responses: Optional[List[SchemaResponse]] = None,
39
- exceptions: Optional[List[SchemaResponse]] = None,
40
- want_description_docstring: bool = True,
64
+ responses: List[ResponseType],
65
+ exceptions: List[Response],
66
+ *,
67
+ overloads: Optional[List["Operation"]] = None,
68
+ public: bool = True,
41
69
  want_tracing: bool = True,
70
+ abstract: bool = False,
42
71
  ) -> None:
43
72
  super().__init__(
44
73
  code_model=code_model,
45
74
  yaml_data=yaml_data,
46
75
  name=name,
47
- description=description,
48
76
  parameters=parameters,
49
- responses=responses,
50
- schema_requests=schema_requests,
51
- summary=summary,
77
+ overloads=overloads,
78
+ abstract=abstract,
79
+ want_tracing=want_tracing,
52
80
  )
53
- self.multiple_content_type_parameters = multiple_content_type_parameters
54
- self.api_versions = api_versions
55
- self.multiple_content_type_parameters = multiple_content_type_parameters
56
- self.exceptions = exceptions or []
57
- self.want_description_docstring = want_description_docstring
58
- self.want_tracing = want_tracing
59
- self._request_builder: Optional[RequestBuilder] = None
81
+ self.overloads: List["Operation"] = overloads or []
82
+ self.responses = responses
83
+ self.public = public
84
+ self.request_builder = request_builder
60
85
  self.deprecated = False
86
+ self.exceptions = exceptions
61
87
 
62
88
  @property
63
- def python_name(self) -> str:
64
- return self.name
65
-
66
- @property
67
- def request_builder(self) -> RequestBuilder:
68
- if not self._request_builder:
69
- raise ValueError(
70
- "You're calling request_builder when you haven't linked up operation to its "
71
- "request builder through the code model"
72
- )
73
- return self._request_builder
74
-
75
- @request_builder.setter
76
- def request_builder(self, r: RequestBuilder) -> None:
77
- self._request_builder = r
78
-
79
- @property
80
- def is_stream_response(self) -> bool:
81
- """Is the response expected to be streamable, like a download."""
82
- return any(response.is_stream_response for response in self.responses)
83
-
84
- @property
85
- def body_kwargs_to_pass_to_request_builder(self) -> List[str]:
86
- return [p.serialized_name for p in self.request_builder.body_kwargs_to_get]
89
+ def operation_type(self) -> str:
90
+ return "operation"
87
91
 
88
92
  @property
89
93
  def has_optional_return_type(self) -> bool:
90
94
  """Has optional return type if there are multiple successful response types where some have
91
95
  bodies and some are None
92
96
  """
93
-
94
- # successful status codes of responses that have bodies
95
- status_codes_for_responses_with_bodies = [
96
- code for code in self.success_status_code
97
- if isinstance(code, int) and self.get_response_from_status(code).has_body
98
- ]
99
-
100
- successful_responses = [
101
- response for response in self.responses
102
- if any(code in self.success_status_code for code in response.status_codes)
103
- ]
104
-
105
- return (
106
- self.has_response_body and
107
- len(successful_responses) > 1 and
108
- len(self.success_status_code) != len(status_codes_for_responses_with_bodies)
109
- )
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
101
+
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
110
+ for response in self.responses
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"
110
121
 
111
122
  @property
112
- def serialization_context(self) -> str:
113
- # FIXME Do the serialization context (XML)
114
- 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)
115
166
 
116
167
  @property
117
168
  def has_response_body(self) -> bool:
118
- """Tell if at least one response has a body.
119
- """
120
- return any(response.has_body or response.is_stream_response for response in self.responses)
169
+ """Tell if at least one response has a body."""
170
+ return any(response.type for response in self.responses)
121
171
 
122
172
  @property
123
173
  def any_response_has_headers(self) -> bool:
124
- return any(response.has_headers for response in self.responses)
174
+ return any(response.headers for response in self.responses)
125
175
 
126
176
  @property
127
- def default_exception(self) -> Optional[str]:
128
- default_excp = [excp for excp in self.exceptions for code in excp.status_codes if code == "default"]
129
- if not default_excp:
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
180
+ ]
181
+ if not default_exceptions:
130
182
  return None
131
- excep_schema = default_excp[0].schema
132
- if isinstance(excep_schema, ObjectSchema):
183
+ excep_schema = default_exceptions[0].type
184
+ if isinstance(excep_schema, ModelType):
133
185
  return f"_models.{excep_schema.name}"
134
- # in this case, it's just an AnySchema
135
- return "\'object\'"
186
+ # in this case, it's just an AnyType
187
+ return "'object'"
136
188
 
137
189
  @property
138
- def status_code_exceptions(self) -> List[SchemaResponse]:
139
- return [excp for excp in self.exceptions if list(excp.status_codes) != ["default"]]
190
+ def non_default_errors(self) -> List[Response]:
191
+ return [e for e in self.exceptions if "default" not in e.status_codes]
140
192
 
141
193
  @property
142
- def status_code_exceptions_status_codes(self) -> List[Union[str, int]]:
194
+ def non_default_error_status_codes(self) -> List[Union[str, int]]:
143
195
  """Actually returns all of the status codes from exceptions (besides default)"""
144
- return list(chain.from_iterable([
145
- excp.status_codes for excp in self.status_code_exceptions
146
- ]))
196
+ return list(
197
+ chain.from_iterable(
198
+ [error.status_codes for error in self.non_default_errors]
199
+ )
200
+ )
147
201
 
148
- def _imports_shared(self, async_mode: bool) -> FileImport: # pylint: disable=unused-argument
202
+ def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport:
149
203
  file_import = FileImport()
150
- file_import.add_submodule_import("typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL)
151
- for param in self.parameters.method:
152
- file_import.merge(param.imports())
153
-
154
- for param in self.multiple_content_type_parameters:
155
- file_import.merge(param.imports())
156
-
157
- for response in self.responses:
158
- file_import.merge(response.imports(self.code_model))
159
- if response.has_body:
160
- file_import.merge(cast(BaseSchema, response.schema).imports())
204
+ file_import.add_submodule_import(
205
+ "typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL
206
+ )
207
+ if not self.abstract:
208
+ for param in self.parameters.method:
209
+ file_import.merge(param.imports(async_mode, **kwargs))
161
210
 
162
- response_types = [r.operation_type_annotation for r in self.responses if r.has_body]
211
+ response_types = [
212
+ r.type_annotation(async_mode=async_mode) for r in self.responses if r.type
213
+ ]
163
214
  if len(set(response_types)) > 1:
164
- file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
165
-
166
- if self.is_stream_response:
167
- file_import.add_submodule_import("typing", "IO", ImportType.STDLIB, TypingSection.CONDITIONAL)
215
+ file_import.add_submodule_import(
216
+ "typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
217
+ )
168
218
  return file_import
169
219
 
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
+ )
231
+ return file_import
170
232
 
171
- def imports_for_multiapi(self, async_mode: bool) -> FileImport: # pylint: disable=unused-argument
172
- return self._imports_shared(async_mode)
173
-
174
- def imports(self, async_mode: bool) -> FileImport:
175
- file_import = self._imports_shared(async_mode)
176
-
177
- # Exceptions
178
- file_import.add_submodule_import("azure.core.exceptions", "map_error", ImportType.AZURECORE)
179
- if self.code_model.options["azure_arm"]:
180
- file_import.add_submodule_import("azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.AZURECORE)
181
- file_import.add_submodule_import("azure.core.exceptions", "HttpResponseError", ImportType.AZURECORE)
182
-
183
- file_import.add_submodule_import("typing", "Callable", ImportType.STDLIB, TypingSection.CONDITIONAL)
184
- file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
185
- file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB, TypingSection.CONDITIONAL)
186
- file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL)
187
- file_import.add_submodule_import("azure.core.pipeline", "PipelineResponse", ImportType.AZURECORE)
188
- file_import.add_submodule_import("azure.core.rest", "HttpRequest", ImportType.AZURECORE)
189
- if async_mode:
190
- file_import.add_submodule_import("azure.core.pipeline.transport", "AsyncHttpResponse", ImportType.AZURECORE)
191
- else:
192
- file_import.add_submodule_import("azure.core.pipeline.transport", "HttpResponse", ImportType.AZURECORE)
193
-
194
- if self.deprecated:
195
- file_import.add_import("warnings", ImportType.STDLIB)
233
+ @staticmethod
234
+ def has_kwargs_to_pop_with_default(
235
+ kwargs_to_pop: List[
236
+ Union[
237
+ Parameter,
238
+ RequestBuilderParameter,
239
+ BodyParameter,
240
+ MultipartBodyParameter,
241
+ ]
242
+ ],
243
+ location: ParameterLocation,
244
+ ) -> bool:
245
+ return any(
246
+ (kwarg.client_default_value or kwarg.optional)
247
+ and kwarg.location == location
248
+ for kwarg in kwargs_to_pop
249
+ )
196
250
 
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()
197
258
  if self.code_model.options["builders_visibility"] != "embedded":
198
- builder_group_name = self.request_builder.builder_group_name
259
+ group_name = request_builder.group_name
199
260
  rest_import_path = "..." if async_mode else ".."
200
- if builder_group_name:
261
+ if group_name:
201
262
  file_import.add_submodule_import(
202
263
  f"{rest_import_path}{self.code_model.rest_layer_name}",
203
- builder_group_name,
264
+ group_name,
204
265
  import_type=ImportType.LOCAL,
205
- alias=f"rest_{builder_group_name}"
266
+ alias=f"rest_{group_name}",
206
267
  )
207
268
  else:
208
269
  file_import.add_submodule_import(
209
270
  rest_import_path,
210
271
  self.code_model.rest_layer_name,
211
272
  import_type=ImportType.LOCAL,
212
- alias="rest"
273
+ alias="rest",
213
274
  )
214
- if self.code_model.options["builders_visibility"] == "embedded" and not async_mode:
215
- file_import.merge(self.request_builder.imports())
216
- if self.code_model.need_request_converter:
217
- relative_path = "..." if async_mode else ".."
218
- file_import.add_submodule_import(
219
- f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
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 ""
220
281
  )
221
- if self.code_model.options["version_tolerant"] and (
222
- self.parameters.has_body or
223
- any(r for r in self.responses if r.has_body)
224
- ):
225
- file_import.define_mypy_type("JSONType", "Any")
226
- if self.code_model.options["tracing"] and self.want_tracing:
227
282
  file_import.add_submodule_import(
228
- f"azure.core.tracing.decorator{'_async' if async_mode else ''}",
229
- f"distributed_trace{'_async' if async_mode else ''}",
230
- ImportType.AZURECORE,
283
+ f"...{self.code_model.operations_folder_name}.{self.filename}{suffix}",
284
+ request_builder.name,
285
+ import_type=ImportType.LOCAL,
231
286
  )
232
287
  return file_import
233
288
 
234
- def _get_body_param_from_body_kwarg(self, body_kwarg: Parameter) -> Parameter:
235
- # determine which of the body parameters returned from m4 corresponds to this body_kwarg
236
- if not self.multiple_content_type_parameters.has_body:
237
- return self.parameters.body[0]
238
- if body_kwarg.serialized_name == "data":
239
- return next(p for p in self.multiple_content_type_parameters.body if p.is_data_input)
240
- if body_kwarg.serialized_name == "files":
241
- return next(p for p in self.multiple_content_type_parameters.body if p.is_multipart)
242
- if body_kwarg.serialized_name == "json":
243
- # first check if there's any non-binary. In the case of multiple content types, there's
244
- # usually one binary (for content), and one schema parameter (for json)
245
- try:
246
- return next(
247
- p for p in self.multiple_content_type_parameters.body
248
- if not isinstance(p.schema, IOSchema)
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))
302
+
303
+ # Exceptions
304
+ if self.abstract:
305
+ file_import.add_import("abc", ImportType.STDLIB)
306
+ else:
307
+ file_import.add_submodule_import(
308
+ "azure.core.exceptions", "map_error", ImportType.AZURECORE
309
+ )
310
+ if self.code_model.options["azure_arm"]:
311
+ file_import.add_submodule_import(
312
+ "azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.AZURECORE
249
313
  )
250
- except StopIteration:
251
- return next(p for p in self.multiple_content_type_parameters.body if p.is_json_parameter)
252
- return self.multiple_content_type_parameters.body[0]
253
-
254
- def link_body_kwargs_to_body_params(self) -> None:
255
- if not self.parameters.has_body:
256
- return
257
- body_kwargs = [
258
- p for p in self.request_builder.parameters.body
259
- if p.content_types
260
- ]
261
- if len(body_kwargs) == 1:
262
- self.parameters.body[0].body_kwargs = [body_kwargs[0]]
263
- return
264
- for body_kwarg in body_kwargs:
265
- body_param = self._get_body_param_from_body_kwarg(body_kwarg)
266
- body_param.body_kwargs.append(body_kwarg)
267
-
268
- def convert_multiple_content_type_parameters(self) -> None:
269
- type_annot = ", ".join([
270
- param.schema.operation_type_annotation
271
- for param in self.multiple_content_type_parameters
272
- ])
273
- docstring_type = " or ".join([
274
- param.schema.docstring_type for param in self.multiple_content_type_parameters
275
- ])
276
- try:
277
- # get an optional param with object first. These params are the top choice
278
- # bc they have more info about how to serialize the body
279
- chosen_parameter = next(
280
- p for p in self.multiple_content_type_parameters
281
- if not p.required and isinstance(p.schema, ObjectSchema)
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,
282
321
  )
283
- except StopIteration: # pylint: disable=broad-except
284
- # otherwise, we get the first optional param, if that exists. If not, we just grab the first one
285
- optional_parameters = [p for p in self.multiple_content_type_parameters if not p.required]
286
- chosen_parameter = (
287
- optional_parameters[0] if optional_parameters else self.multiple_content_type_parameters[0]
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
288
327
  )
289
- if not chosen_parameter:
290
- raise ValueError("You are missing a parameter that has multiple media types")
291
- chosen_parameter.multiple_content_types_type_annot = f"Union[{type_annot}]"
292
- chosen_parameter.multiple_content_types_docstring_type = docstring_type
293
- self.parameters.append(chosen_parameter)
294
328
 
295
- @classmethod
296
- def from_yaml(cls, yaml_data: Dict[str, Any], *, code_model) -> "Operation":
297
- name = yaml_data["language"]["python"]["name"]
298
- _LOGGER.debug("Parsing %s operation", name)
299
-
300
- parameter_creator = get_parameter(code_model).from_yaml
301
- parameter_list_creator = get_parameter_list(code_model)
302
- schema_requests = [SchemaRequest.from_yaml(yaml, code_model=code_model) for yaml in yaml_data["requests"]]
303
- parameters, multiple_content_type_parameters = create_parameters(
304
- yaml_data, code_model, parameter_creator
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
+ ):
335
+ file_import.add_submodule_import(
336
+ "azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE
337
+ )
338
+ if self.deprecated:
339
+ file_import.add_import("warnings", ImportType.STDLIB)
340
+
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
+ )
346
+ if async_mode:
347
+ file_import.add_submodule_import(
348
+ "azure.core.pipeline.transport",
349
+ "AsyncHttpResponse",
350
+ ImportType.AZURECORE,
351
+ )
352
+ else:
353
+ file_import.add_submodule_import(
354
+ "azure.core.pipeline.transport", "HttpResponse", ImportType.AZURECORE
355
+ )
356
+ if (
357
+ self.code_model.options["builders_visibility"] == "embedded"
358
+ and not async_mode
359
+ ):
360
+ file_import.merge(self.request_builder.imports())
361
+ file_import.add_submodule_import(
362
+ "azure.core.pipeline", "PipelineResponse", ImportType.AZURECORE
363
+ )
364
+ file_import.add_submodule_import(
365
+ "azure.core.rest", "HttpRequest", ImportType.AZURECORE
366
+ )
367
+ file_import.add_submodule_import(
368
+ "typing", "Callable", ImportType.STDLIB, TypingSection.CONDITIONAL
305
369
  )
306
- parameter_list = parameter_list_creator(code_model, parameters, schema_requests)
307
- multiple_content_type_parameter_list = parameter_list_creator(
308
- code_model, multiple_content_type_parameters, schema_requests
370
+ file_import.add_submodule_import(
371
+ "typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL
309
372
  )
373
+ file_import.add_submodule_import(
374
+ "typing", "Dict", ImportType.STDLIB, TypingSection.CONDITIONAL
375
+ )
376
+ file_import.add_submodule_import(
377
+ "typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL
378
+ )
379
+ if self.code_model.options["tracing"] and self.want_tracing and not async_mode:
380
+ file_import.add_submodule_import(
381
+ f"azure.core.tracing.decorator",
382
+ f"distributed_trace",
383
+ ImportType.AZURECORE,
384
+ )
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)
391
+ return file_import
310
392
 
311
- if len(parameter_list.content_types) > 1:
312
- for p in parameter_list.parameters:
313
- if p.rest_api_name == "Content-Type":
314
- p.is_keyword_only = True
393
+ def get_response_from_status(
394
+ self, status_code: Optional[Union[str, int]]
395
+ ) -> ResponseType:
396
+ try:
397
+ return next(r for r in self.responses if status_code in r.status_codes)
398
+ except StopIteration:
399
+ raise ValueError(
400
+ f"Incorrect status code {status_code}, operation {self.name}"
401
+ )
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)
425
+
426
+ @classmethod
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", [])
441
+ ]
442
+ abstract = False
443
+ if (
444
+ code_model.options["version_tolerant"]
445
+ and parameter_list.has_body
446
+ and isinstance(parameter_list.body_parameter, MultipartBodyParameter)
447
+ ):
448
+ _LOGGER.warning(
449
+ 'Not going to generate operation "%s" because it has multipart / urlencoded body parameters. '
450
+ "Multipart / urlencoded body parameters are not supported for version tolerant generation right now. "
451
+ 'Please write your own custom operation in the "_patch.py" file '
452
+ "following https://aka.ms/azsdk/python/dpcodegen/python/customize",
453
+ name,
454
+ )
455
+ abstract = True
315
456
 
316
457
  return cls(
317
- code_model=code_model,
318
458
  yaml_data=yaml_data,
459
+ code_model=code_model,
460
+ request_builder=request_builder,
319
461
  name=name,
320
- description=yaml_data["language"]["python"]["description"],
321
- api_versions=set(value_dict["version"] for value_dict in yaml_data["apiVersions"]),
322
462
  parameters=parameter_list,
323
- multiple_content_type_parameters=multiple_content_type_parameter_list,
324
- schema_requests=schema_requests,
325
- summary=yaml_data["language"]["python"].get("summary"),
326
- responses=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("responses", [])],
327
- # Exception with no schema means default exception, we don't store them
328
- exceptions=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("exceptions", []) if "schema" in yaml],
463
+ overloads=overloads,
464
+ responses=responses,
465
+ exceptions=exceptions,
466
+ want_tracing=not yaml_data["isOverload"],
467
+ abstract=abstract,
329
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)