@typespec/http-client-python 0.4.4 → 0.5.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 (137) hide show
  1. package/dist/emitter/emitter.d.ts.map +1 -1
  2. package/dist/emitter/emitter.js +85 -24
  3. package/dist/emitter/emitter.js.map +1 -1
  4. package/dist/emitter/lib.d.ts +1 -0
  5. package/dist/emitter/lib.d.ts.map +1 -1
  6. package/dist/emitter/lib.js +1 -0
  7. package/dist/emitter/lib.js.map +1 -1
  8. package/dist/emitter/run-python3.d.ts +2 -0
  9. package/dist/emitter/run-python3.d.ts.map +1 -0
  10. package/dist/emitter/run-python3.js +19 -0
  11. package/dist/emitter/run-python3.js.map +1 -0
  12. package/dist/emitter/system-requirements.d.ts +17 -0
  13. package/dist/emitter/system-requirements.d.ts.map +1 -0
  14. package/dist/emitter/system-requirements.js +167 -0
  15. package/dist/emitter/system-requirements.js.map +1 -0
  16. package/emitter/src/emitter.ts +88 -23
  17. package/emitter/src/lib.ts +2 -0
  18. package/emitter/src/run-python3.ts +20 -0
  19. package/emitter/src/system-requirements.ts +261 -0
  20. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  21. package/eng/scripts/ci/regenerate.ts +16 -4
  22. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  23. package/eng/scripts/setup/build.ts +16 -0
  24. package/eng/scripts/setup/build_pygen_wheel.py +40 -0
  25. package/eng/scripts/setup/install.py +9 -3
  26. package/eng/scripts/setup/install.ts +32 -0
  27. package/eng/scripts/setup/prepare.py +3 -1
  28. package/eng/scripts/setup/prepare.ts +11 -0
  29. package/eng/scripts/setup/run-python3.ts +1 -6
  30. package/generator/build/lib/pygen/__init__.py +107 -0
  31. package/generator/build/lib/pygen/_version.py +7 -0
  32. package/generator/build/lib/pygen/black.py +71 -0
  33. package/generator/build/lib/pygen/codegen/__init__.py +357 -0
  34. package/generator/build/lib/pygen/codegen/_utils.py +17 -0
  35. package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
  36. package/generator/build/lib/pygen/codegen/models/base.py +186 -0
  37. package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
  38. package/generator/build/lib/pygen/codegen/models/client.py +435 -0
  39. package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
  40. package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
  41. package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
  42. package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
  43. package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
  44. package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
  45. package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
  46. package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
  47. package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
  48. package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
  49. package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
  50. package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
  51. package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
  52. package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
  53. package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
  54. package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
  55. package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
  56. package/generator/build/lib/pygen/codegen/models/property.py +170 -0
  57. package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
  58. package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
  59. package/generator/build/lib/pygen/codegen/models/response.py +348 -0
  60. package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
  61. package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
  62. package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
  63. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
  64. package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
  65. package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
  66. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
  67. package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
  68. package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
  69. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
  70. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
  71. package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  72. package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  73. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
  74. package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
  75. package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  76. package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
  77. package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
  78. package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
  79. package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
  80. package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
  81. package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  82. package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
  83. package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  84. package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  85. package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
  86. package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  87. package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
  88. package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
  89. package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  90. package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  91. package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
  92. package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  93. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
  94. package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
  95. package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
  96. package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
  97. package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  98. package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
  99. package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  100. package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
  101. package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
  102. package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  103. package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  104. package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  105. package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  106. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  107. package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  108. package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  109. package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  110. package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
  111. package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  112. package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  113. package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  114. package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  115. package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
  116. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
  117. package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
  118. package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  119. package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
  120. package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
  121. package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
  122. package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
  123. package/generator/build/lib/pygen/m2r.py +65 -0
  124. package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
  125. package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
  126. package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
  127. package/generator/build/lib/pygen/utils.py +163 -0
  128. package/generator/component-detection-pip-report.json +134 -0
  129. package/generator/dev_requirements.txt +0 -1
  130. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  131. package/generator/pygen.egg-info/PKG-INFO +7 -4
  132. package/generator/pygen.egg-info/requires.txt +7 -4
  133. package/generator/requirements.txt +5 -10
  134. package/generator/setup.py +7 -4
  135. package/generator/test/azure/requirements.txt +1 -1
  136. package/generator/test/unbranded/requirements.txt +1 -1
  137. package/package.json +6 -5
@@ -0,0 +1,509 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ from typing import (
7
+ Dict,
8
+ List,
9
+ Any,
10
+ Optional,
11
+ Union,
12
+ TYPE_CHECKING,
13
+ Generic,
14
+ TypeVar,
15
+ cast,
16
+ )
17
+
18
+ from .request_builder_parameter import RequestBuilderParameter
19
+
20
+ from .utils import OrderedSet, add_to_pylint_disable
21
+ from .base_builder import BaseBuilder
22
+ from .imports import FileImport, ImportType, TypingSection
23
+ from .response import (
24
+ Response,
25
+ PagingResponse,
26
+ LROResponse,
27
+ LROPagingResponse,
28
+ get_response,
29
+ )
30
+ from .parameter import (
31
+ BodyParameter,
32
+ Parameter,
33
+ ParameterLocation,
34
+ )
35
+ from .parameter_list import ParameterList
36
+ from .model_type import ModelType
37
+ from .base import BaseType
38
+ from .request_builder import OverloadedRequestBuilder, RequestBuilder
39
+ from ...utils import xml_serializable, json_serializable, NAME_LENGTH_LIMIT
40
+
41
+ if TYPE_CHECKING:
42
+ from .code_model import CodeModel
43
+ from .client import Client
44
+ from . import OperationType
45
+
46
+ ResponseType = TypeVar(
47
+ "ResponseType",
48
+ bound=Union[Response, PagingResponse, LROResponse, LROPagingResponse],
49
+ )
50
+
51
+
52
+ def is_internal(target: Optional[BaseType]) -> bool:
53
+ return isinstance(target, ModelType) and target.base == "dpg" and target.internal
54
+
55
+
56
+ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instance-attributes
57
+ Generic[ResponseType], BaseBuilder[ParameterList, List["Operation"]]
58
+ ):
59
+ def __init__(
60
+ self,
61
+ yaml_data: Dict[str, Any],
62
+ code_model: "CodeModel",
63
+ client: "Client",
64
+ name: str,
65
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
66
+ parameters: ParameterList,
67
+ responses: List[ResponseType],
68
+ exceptions: List[Response],
69
+ *,
70
+ overloads: Optional[List["Operation"]] = None,
71
+ ) -> None:
72
+ super().__init__(
73
+ code_model=code_model,
74
+ client=client,
75
+ yaml_data=yaml_data,
76
+ name=name,
77
+ parameters=parameters,
78
+ overloads=overloads,
79
+ )
80
+ self.overloads: List["Operation"] = overloads or []
81
+ self.responses = responses
82
+ self.request_builder = request_builder
83
+ self.deprecated = False
84
+ self.exceptions = exceptions
85
+ self.is_lro_initial_operation: bool = self.yaml_data.get("isLroInitialOperation", False)
86
+ self.include_documentation: bool = not self.is_lro_initial_operation
87
+ self.internal: bool = self.yaml_data.get("internal", False)
88
+ if self.internal and not self.name.startswith("_"):
89
+ self.name = "_" + self.name
90
+ self.has_etag: bool = self.yaml_data.get("hasEtag", False)
91
+ self.cross_language_definition_id: Optional[str] = self.yaml_data.get("crossLanguageDefinitionId")
92
+
93
+ @property
94
+ def stream_value(self) -> Union[str, bool]:
95
+ return (
96
+ f'kwargs.pop("stream", {self.has_stream_response})'
97
+ if self.expose_stream_keyword and self.has_response_body
98
+ else self.has_stream_response
99
+ )
100
+
101
+ @property
102
+ def has_form_data_body(self):
103
+ return self.parameters.has_form_data_body
104
+
105
+ @property
106
+ def expose_stream_keyword(self) -> bool:
107
+ return self.yaml_data.get("exposeStreamKeyword", False)
108
+
109
+ @property
110
+ def operation_type(self) -> str:
111
+ return "operation"
112
+
113
+ @property
114
+ def has_optional_return_type(self) -> bool:
115
+ """Has optional return type if there are multiple successful response types where some have
116
+ bodies and some are None
117
+ """
118
+ # means if we have at least one successful response with a body and one without
119
+ successful_response_with_body = any(r for r in self.responses if r.type)
120
+ successful_response_without_body = any(r for r in self.responses if not r.type)
121
+ return successful_response_with_body and successful_response_without_body
122
+
123
+ def response_type_annotation(self, **kwargs) -> str:
124
+ if self.code_model.options["head_as_boolean"] and self.request_builder.method.lower() == "head":
125
+ return "bool"
126
+ response_type_annotations: OrderedSet[str] = {
127
+ response.type_annotation(**kwargs): None for response in self.responses if response.type
128
+ }
129
+ response_str = ", ".join(response_type_annotations.keys())
130
+ if len(response_type_annotations) > 1:
131
+ return f"Union[{response_str}]"
132
+ if self.has_optional_return_type:
133
+ return f"Optional[{response_str}]"
134
+ if self.responses:
135
+ return self.responses[0].type_annotation(**kwargs)
136
+ return "None"
137
+
138
+ def pylint_disable(self, async_mode: bool) -> str:
139
+ retval: str = ""
140
+ if not async_mode and not self.is_overload and self.response_type_annotation(async_mode=False) == "None":
141
+ # doesn't matter if it's async or not
142
+ retval = add_to_pylint_disable(retval, "inconsistent-return-statements")
143
+ if len(self.name) > NAME_LENGTH_LIMIT:
144
+ retval = add_to_pylint_disable(retval, "name-too-long")
145
+ return retval
146
+
147
+ def cls_type_annotation(self, *, async_mode: bool) -> str:
148
+ if self.request_builder.method.lower() == "head" and self.code_model.options["head_as_boolean"]:
149
+ return "ClsType[None]"
150
+ return f"ClsType[{self.response_type_annotation(async_mode=async_mode)}]"
151
+
152
+ def _response_docstring_helper(self, attr_name: str, **kwargs: Any) -> str:
153
+ responses_with_body = [r for r in self.responses if r.type]
154
+ if self.request_builder.method.lower() == "head" and self.code_model.options["head_as_boolean"]:
155
+ return "bool"
156
+ if responses_with_body:
157
+ response_docstring_values: OrderedSet[str] = {
158
+ getattr(response, attr_name)(**kwargs): None for response in responses_with_body
159
+ }
160
+ retval = " or ".join(response_docstring_values.keys())
161
+ if self.has_optional_return_type:
162
+ retval += " or None"
163
+ return retval
164
+ if self.responses:
165
+ return getattr(self.responses[0], attr_name)(**kwargs)
166
+ return "None"
167
+
168
+ def response_docstring_text(self, **kwargs) -> str:
169
+ retval = self._response_docstring_helper("docstring_text", **kwargs)
170
+ if not self.code_model.options["version_tolerant"]:
171
+ retval += " or the result of cls(response)"
172
+ if self.code_model.options["models_mode"] == "dpg" and any(
173
+ isinstance(r.type, ModelType) for r in self.responses
174
+ ):
175
+ r = next(r for r in self.responses if isinstance(r.type, ModelType))
176
+ item_type = getattr(r, "item_type", getattr(r, "type"))
177
+ if item_type:
178
+ type_name = item_type.docstring_text(**kwargs)
179
+ retval += f". The {type_name} is compatible with MutableMapping"
180
+ return retval
181
+
182
+ def response_docstring_type(self, **kwargs) -> str:
183
+ return self._response_docstring_helper("docstring_type", **kwargs)
184
+
185
+ @property
186
+ def has_response_body(self) -> bool:
187
+ """Tell if at least one response has a body."""
188
+ return any(response.type for response in self.responses)
189
+
190
+ @property
191
+ def any_response_has_headers(self) -> bool:
192
+ return any(response.headers for response in self.responses)
193
+
194
+ @property
195
+ def default_error_deserialization(self) -> Optional[str]:
196
+ default_exceptions = [e for e in self.exceptions if "default" in e.status_codes and e.type]
197
+ if not default_exceptions:
198
+ return None
199
+ exception_schema = default_exceptions[0].type
200
+ if isinstance(exception_schema, ModelType):
201
+ return exception_schema.type_annotation(skip_quote=True)
202
+ return None if self.code_model.options["models_mode"] == "dpg" else "'object'"
203
+
204
+ @property
205
+ def non_default_errors(self) -> List[Response]:
206
+ return [
207
+ e for e in self.exceptions if "default" not in e.status_codes and e.type and isinstance(e.type, ModelType)
208
+ ]
209
+
210
+ def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport: # pylint: disable=unused-argument
211
+ file_import = FileImport(self.code_model)
212
+ file_import.add_submodule_import("typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL)
213
+
214
+ response_types = [r.type_annotation(async_mode=async_mode, operation=self) for r in self.responses if r.type]
215
+ if len(set(response_types)) > 1:
216
+ file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
217
+ if self.added_on:
218
+ file_import.add_submodule_import(
219
+ f"{'.' if async_mode else ''}.._validation",
220
+ "api_version_validation",
221
+ ImportType.LOCAL,
222
+ )
223
+ return file_import
224
+
225
+ def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
226
+ if self.abstract:
227
+ return FileImport(self.code_model)
228
+ file_import = self._imports_shared(async_mode, **kwargs)
229
+ for param in self.parameters.method:
230
+ file_import.merge(
231
+ param.imports_for_multiapi(
232
+ async_mode,
233
+ operation=self,
234
+ **kwargs,
235
+ )
236
+ )
237
+ for response in self.responses:
238
+ file_import.merge(response.imports_for_multiapi(async_mode=async_mode, operation=self, **kwargs))
239
+ if self.code_model.options["models_mode"]:
240
+ for exception in self.exceptions:
241
+ file_import.merge(exception.imports_for_multiapi(async_mode=async_mode, operation=self, **kwargs))
242
+ return file_import
243
+
244
+ @staticmethod
245
+ def has_kwargs_to_pop_with_default(
246
+ kwargs_to_pop: List[
247
+ Union[
248
+ Parameter,
249
+ RequestBuilderParameter,
250
+ BodyParameter,
251
+ ]
252
+ ],
253
+ location: ParameterLocation,
254
+ ) -> bool:
255
+ return any(
256
+ (kwarg.client_default_value or kwarg.optional) and kwarg.location == location for kwarg in kwargs_to_pop
257
+ )
258
+
259
+ @property
260
+ def need_validation(self) -> bool:
261
+ """Whether we need parameter / operation validation. For API version."""
262
+ return bool(self.added_on) or any(p for p in self.parameters if p.added_on)
263
+
264
+ def get_request_builder_import(
265
+ self,
266
+ request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
267
+ async_mode: bool,
268
+ ) -> FileImport:
269
+ """Helper method to get a request builder import."""
270
+ file_import = FileImport(self.code_model)
271
+ if self.code_model.options["builders_visibility"] != "embedded":
272
+ group_name = request_builder.group_name
273
+ rest_import_path = "..." if async_mode else ".."
274
+ if group_name:
275
+ file_import.add_submodule_import(
276
+ f"{rest_import_path}{self.code_model.rest_layer_name}",
277
+ group_name,
278
+ import_type=ImportType.LOCAL,
279
+ alias=f"rest_{group_name}",
280
+ )
281
+ else:
282
+ file_import.add_submodule_import(
283
+ rest_import_path,
284
+ self.code_model.rest_layer_name,
285
+ import_type=ImportType.LOCAL,
286
+ alias="rest",
287
+ )
288
+ if self.code_model.options["builders_visibility"] == "embedded" and async_mode:
289
+ file_import.add_submodule_import(
290
+ f"...{self.code_model.operations_folder_name}.{self.filename}",
291
+ request_builder.name,
292
+ import_type=ImportType.LOCAL,
293
+ )
294
+ return file_import
295
+
296
+ def imports( # pylint: disable=too-many-branches, disable=too-many-statements
297
+ self, async_mode: bool, **kwargs: Any
298
+ ) -> FileImport:
299
+ if self.abstract:
300
+ return FileImport(self.code_model)
301
+ file_import = self._imports_shared(async_mode, **kwargs)
302
+
303
+ for param in self.parameters.method:
304
+ file_import.merge(
305
+ param.imports(
306
+ async_mode,
307
+ operation=self,
308
+ **kwargs,
309
+ )
310
+ )
311
+ for response in self.responses:
312
+ file_import.merge(response.imports(async_mode=async_mode, operation=self, **kwargs))
313
+ if self.code_model.options["models_mode"]:
314
+ for exception in self.exceptions:
315
+ file_import.merge(exception.imports(async_mode=async_mode, **kwargs))
316
+
317
+ if self.parameters.has_body and self.parameters.body_parameter.flattened:
318
+ file_import.merge(self.parameters.body_parameter.type.imports(operation=self, **kwargs))
319
+ if not async_mode:
320
+ for param in self.parameters.headers:
321
+ if param.wire_name.lower() == "repeatability-request-id":
322
+ file_import.add_import("uuid", ImportType.STDLIB)
323
+ elif param.wire_name.lower() == "repeatability-first-sent":
324
+ file_import.add_import("datetime", ImportType.STDLIB)
325
+
326
+ # Exceptions
327
+ errors = [
328
+ "map_error",
329
+ "HttpResponseError",
330
+ "ClientAuthenticationError",
331
+ "ResourceNotFoundError",
332
+ "ResourceExistsError",
333
+ "ResourceNotModifiedError",
334
+ ]
335
+ if self.stream_value:
336
+ errors.extend(["StreamConsumedError", "StreamClosedError"])
337
+ for error in errors:
338
+ file_import.add_submodule_import("exceptions", error, ImportType.SDKCORE)
339
+ if self.code_model.options["azure_arm"]:
340
+ file_import.add_submodule_import("azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.SDKCORE)
341
+ file_import.add_mutable_mapping_import()
342
+
343
+ if self.has_kwargs_to_pop_with_default(
344
+ self.parameters.kwargs_to_pop, ParameterLocation.HEADER # type: ignore
345
+ ) or self.has_kwargs_to_pop_with_default(
346
+ self.parameters.kwargs_to_pop, ParameterLocation.QUERY # type: ignore
347
+ ):
348
+ file_import.add_submodule_import(
349
+ "utils",
350
+ "case_insensitive_dict",
351
+ ImportType.SDKCORE,
352
+ )
353
+ if self.deprecated:
354
+ file_import.add_import("warnings", ImportType.STDLIB)
355
+
356
+ relative_path = "..." if async_mode else ".."
357
+ if self.has_etag:
358
+ file_import.add_submodule_import(
359
+ "exceptions",
360
+ "ResourceModifiedError",
361
+ ImportType.SDKCORE,
362
+ )
363
+ if not async_mode:
364
+ file_import.add_submodule_import(f"{relative_path}_vendor", "prep_if_match", ImportType.LOCAL)
365
+ file_import.add_submodule_import(f"{relative_path}_vendor", "prep_if_none_match", ImportType.LOCAL)
366
+ if async_mode:
367
+ file_import.add_submodule_import(
368
+ "rest",
369
+ "AsyncHttpResponse",
370
+ ImportType.SDKCORE,
371
+ )
372
+ else:
373
+ file_import.add_submodule_import(
374
+ "rest",
375
+ "HttpResponse",
376
+ ImportType.SDKCORE,
377
+ )
378
+ if self.code_model.options["builders_visibility"] == "embedded" and not async_mode:
379
+ file_import.merge(self.request_builder.imports())
380
+ file_import.add_submodule_import(
381
+ f"{'' if self.code_model.is_azure_flavor else 'runtime.'}pipeline",
382
+ "PipelineResponse",
383
+ ImportType.SDKCORE,
384
+ )
385
+ file_import.add_submodule_import("rest", "HttpRequest", ImportType.SDKCORE)
386
+ file_import.add_submodule_import("typing", "Callable", ImportType.STDLIB, TypingSection.CONDITIONAL)
387
+ file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
388
+ file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB, TypingSection.CONDITIONAL)
389
+ file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL)
390
+ if self.code_model.options["tracing"] and self.want_tracing and not async_mode:
391
+ file_import.add_submodule_import(
392
+ "azure.core.tracing.decorator",
393
+ "distributed_trace",
394
+ ImportType.SDKCORE,
395
+ )
396
+ file_import.merge(self.get_request_builder_import(self.request_builder, async_mode))
397
+ if self.overloads:
398
+ file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
399
+ if self.code_model.options["models_mode"] == "dpg":
400
+ if self.parameters.has_body:
401
+ if self.has_form_data_body:
402
+ file_import.add_submodule_import(relative_path, "_model_base", ImportType.LOCAL)
403
+ elif xml_serializable(self.parameters.body_parameter.default_content_type):
404
+ file_import.add_submodule_import(
405
+ f"{relative_path}_model_base",
406
+ "_get_element",
407
+ ImportType.LOCAL,
408
+ )
409
+ elif json_serializable(self.parameters.body_parameter.default_content_type):
410
+ file_import.add_submodule_import(
411
+ f"{relative_path}_model_base",
412
+ "SdkJSONEncoder",
413
+ ImportType.LOCAL,
414
+ )
415
+ file_import.add_import("json", ImportType.STDLIB)
416
+ if any(xml_serializable(str(r.default_content_type)) for r in self.responses):
417
+ file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize_xml", ImportType.LOCAL)
418
+ elif any(r.type for r in self.responses):
419
+ file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
420
+ if self.default_error_deserialization or self.non_default_errors:
421
+ file_import.add_submodule_import(
422
+ f"{relative_path}_model_base", "_failsafe_deserialize", ImportType.LOCAL
423
+ )
424
+ return file_import
425
+
426
+ def get_response_from_status(self, status_code: Optional[Union[str, int]]) -> ResponseType:
427
+ try:
428
+ return next(r for r in self.responses if status_code in r.status_codes)
429
+ except StopIteration as exc:
430
+ raise ValueError(f"Incorrect status code {status_code}, operation {self.name}") from exc
431
+
432
+ @property
433
+ def success_status_codes(self) -> List[Union[int, str, List[int]]]:
434
+ """The list of all successfull status code."""
435
+ return sorted([code for response in self.responses for code in response.status_codes])
436
+
437
+ @property
438
+ def filename(self) -> str:
439
+ basename = self.group_name
440
+ if basename == "":
441
+ # in a mixin
442
+ basename = self.code_model.clients[0].legacy_filename
443
+
444
+ if basename == "operations" or self.code_model.options["combine_operation_files"]:
445
+ return "_operations"
446
+ return f"_{basename}_operations"
447
+
448
+ @property
449
+ def has_stream_response(self) -> bool:
450
+ return any(r.is_stream_response for r in self.responses)
451
+
452
+ @classmethod
453
+ def get_request_builder(cls, yaml_data: Dict[str, Any], client: "Client"):
454
+ return client.lookup_request_builder(id(yaml_data))
455
+
456
+ @classmethod
457
+ def from_yaml(
458
+ cls,
459
+ yaml_data: Dict[str, Any],
460
+ code_model: "CodeModel",
461
+ client: "Client",
462
+ ):
463
+ name = yaml_data["name"]
464
+ request_builder = cls.get_request_builder(yaml_data, client)
465
+ responses = [cast(ResponseType, get_response(r, code_model)) for r in yaml_data["responses"]]
466
+ exceptions = [Response.from_yaml(e, code_model) for e in yaml_data["exceptions"]]
467
+ parameter_list = ParameterList.from_yaml(yaml_data, code_model)
468
+ overloads = [cls.from_yaml(overload, code_model, client) for overload in yaml_data.get("overloads", [])]
469
+
470
+ return cls(
471
+ yaml_data=yaml_data,
472
+ code_model=code_model,
473
+ client=client,
474
+ request_builder=request_builder,
475
+ name=name,
476
+ parameters=parameter_list,
477
+ overloads=overloads,
478
+ responses=responses,
479
+ exceptions=exceptions,
480
+ )
481
+
482
+
483
+ class Operation(OperationBase[Response]):
484
+ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
485
+ file_import = super().imports(async_mode, **kwargs)
486
+ if self.abstract:
487
+ return file_import
488
+ if async_mode and self.code_model.options["tracing"] and self.want_tracing:
489
+ file_import.add_submodule_import(
490
+ "azure.core.tracing.decorator_async",
491
+ "distributed_trace_async",
492
+ ImportType.SDKCORE,
493
+ )
494
+ if self.has_response_body and not self.has_optional_return_type and not self.code_model.options["models_mode"]:
495
+ file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
496
+
497
+ return file_import
498
+
499
+
500
+ def get_operation(yaml_data: Dict[str, Any], code_model: "CodeModel", client: "Client") -> "OperationType":
501
+ if yaml_data["discriminator"] == "lropaging":
502
+ from .lro_paging_operation import LROPagingOperation as OperationCls
503
+ elif yaml_data["discriminator"] == "lro":
504
+ from .lro_operation import LROOperation as OperationCls # type: ignore
505
+ elif yaml_data["discriminator"] == "paging":
506
+ from .paging_operation import PagingOperation as OperationCls # type: ignore
507
+ else:
508
+ from . import Operation as OperationCls # type: ignore
509
+ return OperationCls.from_yaml(yaml_data, code_model, client)
@@ -0,0 +1,184 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ from typing import Dict, List, Any, TYPE_CHECKING
7
+
8
+ from .utils import OrderedSet
9
+
10
+ from .base import BaseModel
11
+ from .operation import get_operation
12
+ from .imports import FileImport, ImportType, TypingSection
13
+ from .utils import add_to_pylint_disable
14
+ from .lro_operation import LROOperation
15
+ from .lro_paging_operation import LROPagingOperation
16
+ from ...utils import NAME_LENGTH_LIMIT
17
+
18
+ if TYPE_CHECKING:
19
+ from .code_model import CodeModel
20
+ from .client import Client
21
+ from . import OperationType
22
+
23
+
24
+ class OperationGroup(BaseModel):
25
+ """Represent an operation group."""
26
+
27
+ def __init__(
28
+ self,
29
+ yaml_data: Dict[str, Any],
30
+ code_model: "CodeModel",
31
+ client: "Client",
32
+ operations: List["OperationType"],
33
+ api_versions: List[str],
34
+ ) -> None:
35
+ super().__init__(yaml_data, code_model)
36
+ self.client = client
37
+ self.class_name: str = yaml_data["className"]
38
+ self.identify_name: str = yaml_data["identifyName"]
39
+ self.property_name: str = yaml_data["propertyName"]
40
+ self.operations = operations
41
+ self.api_versions = api_versions
42
+ self.operation_groups: List[OperationGroup] = []
43
+ if self.code_model.options["show_operations"]:
44
+ self.operation_groups = [
45
+ OperationGroup.from_yaml(op_group, code_model, client)
46
+ for op_group in self.yaml_data.get("operationGroups", [])
47
+ ]
48
+ self.link_lro_initial_operations()
49
+
50
+ @property
51
+ def has_abstract_operations(self) -> bool:
52
+ return any(o for o in self.operations if o.abstract) or any(
53
+ operation_group.has_abstract_operations for operation_group in self.operation_groups
54
+ )
55
+
56
+ @property
57
+ def has_non_abstract_operations(self) -> bool:
58
+ return any(o for o in self.operations if not o.abstract) or any(
59
+ operation_group.has_non_abstract_operations for operation_group in self.operation_groups
60
+ )
61
+
62
+ @property
63
+ def base_class(self) -> str:
64
+ base_classes: List[str] = []
65
+ if self.is_mixin:
66
+ base_classes.append(f"{self.client.name}MixinABC")
67
+ return ", ".join(base_classes)
68
+
69
+ def imports_for_multiapi(self, async_mode: bool) -> FileImport:
70
+ file_import = FileImport(self.code_model)
71
+ relative_path = ".." if async_mode else "."
72
+ for operation in self.operations:
73
+ file_import.merge(operation.imports_for_multiapi(async_mode, relative_path=relative_path))
74
+ if (self.code_model.model_types or self.code_model.enums) and self.code_model.options[
75
+ "models_mode"
76
+ ] == "msrest":
77
+ file_import.add_submodule_import(relative_path, "models", ImportType.LOCAL, alias="_models")
78
+ return file_import
79
+
80
+ def pylint_disable(self) -> str:
81
+ retval: str = ""
82
+ if self.has_abstract_operations:
83
+ retval = add_to_pylint_disable(retval, "abstract-class-instantiated")
84
+ if len(self.operations) > 20:
85
+ retval = add_to_pylint_disable(retval, "too-many-public-methods")
86
+ if len(self.class_name) > NAME_LENGTH_LIMIT:
87
+ retval = add_to_pylint_disable(retval, "name-too-long")
88
+ if len(self.operation_groups) > 6:
89
+ retval = add_to_pylint_disable(retval, "too-many-instance-attributes")
90
+ return retval
91
+
92
+ @property
93
+ def need_validation(self) -> bool:
94
+ """Whether any of its operations need validation"""
95
+ return any(o for o in self.operations if o.need_validation)
96
+
97
+ def imports(self, async_mode: bool) -> FileImport:
98
+ file_import = FileImport(self.code_model)
99
+
100
+ relative_path = ("..." if async_mode else "..") + ("." if self.client.is_subclient else "")
101
+ for operation in self.operations:
102
+ file_import.merge(operation.imports(async_mode, relative_path=relative_path))
103
+ if not self.code_model.options["combine_operation_files"]:
104
+ for og in self.operation_groups:
105
+ file_import.add_submodule_import(
106
+ ".",
107
+ og.class_name,
108
+ ImportType.LOCAL,
109
+ )
110
+ # for multiapi
111
+ if (
112
+ (self.code_model.public_model_types)
113
+ and self.code_model.options["models_mode"] == "msrest"
114
+ and not self.is_mixin
115
+ ):
116
+ file_import.add_submodule_import(relative_path, "models", ImportType.LOCAL, alias="_models")
117
+ if self.is_mixin:
118
+ file_import.add_submodule_import(".._vendor", f"{self.client.name}MixinABC", ImportType.LOCAL)
119
+ if self.has_abstract_operations:
120
+ file_import.add_submodule_import(".._vendor", "raise_if_not_implemented", ImportType.LOCAL)
121
+ if all(o.abstract for o in self.operations):
122
+ return file_import
123
+ file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL)
124
+ file_import.define_mypy_type("T", "TypeVar('T')")
125
+ type_value = "Optional[Callable[[PipelineResponse[HttpRequest, {}HttpResponse], T, Dict[str, Any]], Any]]"
126
+ file_import.define_mypy_type("ClsType", type_value.format(""), type_value.format("Async"))
127
+ return file_import
128
+
129
+ @property
130
+ def filename(self) -> str:
131
+ return self.operations[0].filename
132
+
133
+ @property
134
+ def is_mixin(self) -> bool:
135
+ """The operation group with no name is the direct client methods."""
136
+ return self.identify_name == ""
137
+
138
+ def link_lro_initial_operations(self) -> None:
139
+ """Link each LRO operation to its initial operation"""
140
+ for operation_group in self.operation_groups:
141
+ for operation in operation_group.operations:
142
+ if isinstance(operation, (LROOperation, LROPagingOperation)):
143
+ operation.initial_operation = self.lookup_operation(id(operation.yaml_data["initialOperation"]))
144
+
145
+ def lookup_operation(self, operation_id: int) -> "OperationType":
146
+ try:
147
+ return next(o for og in self.operation_groups for o in og.operations if id(o.yaml_data) == operation_id)
148
+ except StopIteration as exc:
149
+ raise KeyError(f"No operation with id {operation_id} found.") from exc
150
+
151
+ @property
152
+ def lro_operations(self) -> List["OperationType"]:
153
+ return [operation for operation in self.operations if operation.operation_type in ("lro", "lropaging")] + [
154
+ operation for operation_group in self.operation_groups for operation in operation_group.lro_operations
155
+ ]
156
+
157
+ @property
158
+ def has_operations(self) -> bool:
159
+ return any(operation_group.has_operations for operation_group in self.operation_groups) or bool(self.operations)
160
+
161
+ @property
162
+ def has_form_data_body(self) -> bool:
163
+ operations = self.operations + [o for og in self.operation_groups for o in og.operations]
164
+ return any(operation.has_form_data_body for operation in operations)
165
+
166
+ @classmethod
167
+ def from_yaml(
168
+ cls,
169
+ yaml_data: Dict[str, Any],
170
+ code_model: "CodeModel",
171
+ client: "Client",
172
+ ) -> "OperationGroup":
173
+ operations = [get_operation(o, code_model, client) for o in yaml_data["operations"]]
174
+ api_versions: OrderedSet[str] = {}
175
+ for operation in operations:
176
+ for api_version in operation.api_versions:
177
+ api_versions[api_version] = None
178
+ return cls(
179
+ yaml_data=yaml_data,
180
+ code_model=code_model,
181
+ client=client,
182
+ operations=operations,
183
+ api_versions=list(api_versions.keys()),
184
+ )