@autorest/python 5.10.0 → 5.12.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 (57) hide show
  1. package/ChangeLog.md +74 -0
  2. package/autorest/codegen/__init__.py +7 -7
  3. package/autorest/codegen/models/__init__.py +2 -1
  4. package/autorest/codegen/models/base_builder.py +16 -8
  5. package/autorest/codegen/models/client.py +5 -3
  6. package/autorest/codegen/models/code_model.py +19 -5
  7. package/autorest/codegen/models/imports.py +7 -0
  8. package/autorest/codegen/models/lro_operation.py +7 -3
  9. package/autorest/codegen/models/object_schema.py +3 -3
  10. package/autorest/codegen/models/operation.py +69 -38
  11. package/autorest/codegen/models/operation_group.py +13 -8
  12. package/autorest/codegen/models/paging_operation.py +5 -2
  13. package/autorest/codegen/models/parameter.py +46 -13
  14. package/autorest/codegen/models/parameter_list.py +62 -70
  15. package/autorest/codegen/models/primitive_schemas.py +11 -0
  16. package/autorest/codegen/models/request_builder.py +21 -8
  17. package/autorest/codegen/models/request_builder_parameter.py +9 -5
  18. package/autorest/codegen/models/request_builder_parameter_list.py +179 -77
  19. package/autorest/codegen/models/rest.py +3 -2
  20. package/autorest/codegen/models/schema_request.py +4 -17
  21. package/autorest/codegen/models/schema_response.py +4 -4
  22. package/autorest/codegen/serializers/__init__.py +57 -53
  23. package/autorest/codegen/serializers/builder_serializer.py +76 -46
  24. package/autorest/codegen/serializers/client_serializer.py +37 -9
  25. package/autorest/codegen/serializers/general_serializer.py +7 -5
  26. package/autorest/codegen/serializers/import_serializer.py +22 -7
  27. package/autorest/codegen/serializers/metadata_serializer.py +2 -2
  28. package/autorest/codegen/serializers/model_base_serializer.py +3 -3
  29. package/autorest/codegen/serializers/model_generic_serializer.py +1 -1
  30. package/autorest/codegen/serializers/model_python3_serializer.py +1 -1
  31. package/autorest/codegen/serializers/operation_groups_serializer.py +70 -0
  32. package/autorest/codegen/serializers/operations_init_serializer.py +34 -2
  33. package/autorest/codegen/serializers/patch_serializer.py +15 -0
  34. package/autorest/codegen/serializers/rest_serializer.py +9 -4
  35. package/autorest/codegen/serializers/utils.py +2 -2
  36. package/autorest/codegen/templates/config.py.jinja2 +1 -11
  37. package/autorest/codegen/templates/init.py.jinja2 +4 -7
  38. package/autorest/codegen/templates/metadata.json.jinja2 +2 -2
  39. package/autorest/codegen/templates/model_init.py.jinja2 +1 -1
  40. package/autorest/codegen/templates/{operations_class.py.jinja2 → operation_group.py.jinja2} +2 -0
  41. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +26 -0
  42. package/autorest/codegen/templates/operation_tools.jinja2 +7 -0
  43. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +13 -0
  44. package/autorest/codegen/templates/patch.py.jinja2 +31 -0
  45. package/autorest/codegen/templates/request_builder.py.jinja2 +7 -2
  46. package/autorest/codegen/templates/request_builders.py.jinja2 +3 -3
  47. package/autorest/codegen/templates/setup.py.jinja2 +1 -1
  48. package/autorest/multiapi/serializers/__init__.py +3 -3
  49. package/autorest/multiapi/serializers/import_serializer.py +5 -5
  50. package/autorest/namer/name_converter.py +48 -3
  51. package/package.json +2 -2
  52. package/setup.py +1 -0
  53. package/autorest/codegen/serializers/operation_group_serializer.py +0 -71
  54. package/autorest/codegen/templates/operations_class_mixin.py.jinja2 +0 -16
  55. package/autorest/codegen/templates/operations_container.py.jinja2 +0 -42
  56. package/autorest/codegen/templates/operations_container_init.py.jinja2 +0 -24
  57. package/autorest/codegen/templates/operations_container_mixin.py.jinja2 +0 -23
package/ChangeLog.md CHANGED
@@ -1,5 +1,79 @@
1
1
  # Change Log
2
2
 
3
+ ### 2021-12-06 - 5.12.0
4
+
5
+ | Library | Min Version
6
+ | --------------- | -------
7
+ |`@autorest/core` | `3.6.2`
8
+ |`@autorest/modelerfour` | `4.19.1`
9
+ |`azure-core` dep of generated code | `1.20.1`
10
+ |`msrest` dep of generated code | `0.6.21`
11
+ |`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0`
12
+
13
+ **Breaking Changes in Version Tolerant Generation**
14
+
15
+ - Remove metadata property for version tolerant and low level client generations #1090
16
+ - Generate SDKs with `--python3-only` defaulting to `True` for version tolerant and low level client #1087
17
+
18
+ **New Features**
19
+
20
+ - Generate a `_patch.py` file if one does not exist. These files are used to customize the generated code #1092
21
+
22
+ **Bug Fixes**
23
+
24
+ - Can now handle body params with names `json`, `content`, `data`, and `files` #1081
25
+ - Improve generated templates for `data` and `files` input body params by adding quotes around the keys #1082
26
+ - Using flag `--python3-only` will get you typed sync client and config files #1085
27
+ - Pin `mistune` dependency to less than `2.x.x` so autorest can be successfully installed #1106
28
+
29
+ ### 2021-11-05 - 5.11.2
30
+
31
+ | Library | Min Version
32
+ | --------------- | -------
33
+ |`@autorest/core` | `3.6.2`
34
+ |`@autorest/modelerfour` | `4.19.1`
35
+ |`azure-core` dep of generated code | `1.20.0`
36
+ |`msrest` dep of generated code | `0.6.21`
37
+ |`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0`
38
+
39
+ **Bug Fixes**
40
+
41
+ - Respect no client side validation for low level client generations #1080
42
+
43
+ ### 2021-11-05 - 5.11.1
44
+
45
+ | Library | Min Version
46
+ | --------------- | -------
47
+ |`@autorest/core` | `3.6.2`
48
+ |`@autorest/modelerfour` | `4.19.1`
49
+ |`azure-core` dep of generated code | `1.20.0`
50
+ |`msrest` dep of generated code | `0.6.21`
51
+ |`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0`
52
+
53
+ **Bug Fixes**
54
+
55
+ - Hide mixin operations for version tolerant generation #1071
56
+
57
+ ### 2021-11-04 - 5.11.0
58
+
59
+ | Library | Min Version
60
+ | --------------- | -------
61
+ |`@autorest/core` | `3.6.2`
62
+ |`@autorest/modelerfour` | `4.19.1`
63
+ |`azure-core` dep of generated code | `1.20.0`
64
+ |`msrest` dep of generated code | `0.6.21`
65
+ |`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0`
66
+
67
+ **New Features**
68
+
69
+ - Add `_patch.py` support for `aio` folder #1070
70
+
71
+ **Bug Fixes**
72
+
73
+ - Fix documentation for HEAD calls that perform boolean checks on returned status codes in version tolerant code #1072
74
+ - Fix body grouping by content types for binary bodies #1076
75
+ - Fix default content type determination #1078
76
+
3
77
  ### 2021-11-01 - 5.10.0
4
78
 
5
79
  | Library | Min Version
@@ -31,9 +31,9 @@ def _build_convenience_layer(yaml_data: Dict[str, Any], code_model: CodeModel) -
31
31
  code_model.sort_schemas()
32
32
 
33
33
  if code_model.options["show_operations"]:
34
- code_model.link_operation_to_request_builder()
35
34
  code_model.add_schema_link_to_operation()
36
- code_model.generate_single_parameter_from_multiple_media_types_operation()
35
+ code_model.generate_single_parameter_from_multiple_content_types_operation()
36
+ code_model.link_operation_to_request_builder()
37
37
  # LRO operation
38
38
  code_model.format_lro_operations()
39
39
  code_model.remove_next_operation()
@@ -60,7 +60,7 @@ def _validate_code_model_options(options: Dict[str, Any]) -> None:
60
60
  "to 'public' or 'hidden'."
61
61
  )
62
62
 
63
- if not options["show_operations"] and options["add_python_3_operation_files"]:
63
+ if not options["show_operations"] and options["add_python3_operation_files"]:
64
64
  raise ValueError(
65
65
  "Can not add typed sync operation files if you are not showing operations. "
66
66
  "If you want typed synced operation files, you have to add flag "
@@ -256,7 +256,7 @@ class CodeGenerator(Plugin):
256
256
  version_tolerant = self._autorestapi.get_boolean_value("version-tolerant", False)
257
257
  show_operations = self._autorestapi.get_boolean_value("show-operations", not low_level_client)
258
258
  models_mode_default = "none" if low_level_client or version_tolerant else "msrest"
259
- python_3_only = self._autorestapi.get_boolean_value("python3-only", False)
259
+ python3_only = self._autorestapi.get_boolean_value("python3-only", low_level_client or version_tolerant)
260
260
 
261
261
  options: Dict[str, Any] = {
262
262
  "azure_arm": azure_arm,
@@ -282,13 +282,13 @@ class CodeGenerator(Plugin):
282
282
  "only_path_and_body_params_positional": self._autorestapi.get_boolean_value(
283
283
  "only-path-and-body-params-positional", low_level_client or version_tolerant
284
284
  ),
285
- "add_python_3_operation_files": self._autorestapi.get_boolean_value(
286
- "add-python3-operation-files", python_3_only
285
+ "add_python3_operation_files": self._autorestapi.get_boolean_value(
286
+ "add-python3-operation-files", python3_only and not low_level_client
287
287
  ),
288
288
  "version_tolerant": version_tolerant,
289
289
  "low_level_client": low_level_client,
290
290
  "combine_operation_files": self._autorestapi.get_boolean_value("combine-operation-files", version_tolerant),
291
- "python_3_only": python_3_only,
291
+ "python3_only": python3_only,
292
292
  }
293
293
 
294
294
  if options["builders_visibility"] is None:
@@ -10,7 +10,7 @@ from .credential_schema import AzureKeyCredentialSchema, TokenCredentialSchema
10
10
  from .object_schema import ObjectSchema, get_object_schema, HiddenModelObjectSchema
11
11
  from .dictionary_schema import DictionarySchema
12
12
  from .list_schema import ListSchema
13
- from .primitive_schemas import get_primitive_schema, AnySchema, PrimitiveSchema
13
+ from .primitive_schemas import get_primitive_schema, AnySchema, PrimitiveSchema, IOSchema
14
14
  from .enum_schema import EnumSchema, HiddenModelEnumSchema, get_enum_schema
15
15
  from .base_schema import BaseSchema
16
16
  from .constant_schema import ConstantSchema
@@ -62,6 +62,7 @@ __all__ = [
62
62
  "RequestBuilderParameter",
63
63
  "HiddenModelObjectSchema",
64
64
  "ParameterStyle",
65
+ "IOSchema",
65
66
  ]
66
67
 
67
68
  def _generate_as_object_schema(yaml_data: Dict[str, Any]) -> bool:
@@ -6,6 +6,7 @@
6
6
  from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
7
7
  from .base_model import BaseModel
8
8
  from .schema_response import SchemaResponse
9
+ from .schema_request import SchemaRequest
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from . import ParameterListType
@@ -13,11 +14,16 @@ if TYPE_CHECKING:
13
14
 
14
15
  _M4_HEADER_PARAMETERS = ["content_type", "accept"]
15
16
 
16
- def create_parameters(yaml_data: Dict[str, Any], code_model, parameter_creator: Callable):
17
+ def create_parameters(
18
+ yaml_data: Dict[str, Any], code_model, parameter_creator: Callable
19
+ ):
17
20
  multiple_requests = len(yaml_data["requests"]) > 1
18
21
 
19
- multiple_media_type_parameters = []
20
- parameters = [parameter_creator(yaml, code_model=code_model) for yaml in yaml_data.get("parameters", [])]
22
+ multiple_content_type_parameters = []
23
+ parameters = [
24
+ parameter_creator(yaml, code_model=code_model)
25
+ for yaml in yaml_data.get("parameters", [])
26
+ ]
21
27
 
22
28
  for request in yaml_data["requests"]:
23
29
  for yaml in request.get("parameters", []):
@@ -26,14 +32,14 @@ def create_parameters(yaml_data: Dict[str, Any], code_model, parameter_creator:
26
32
  if name in _M4_HEADER_PARAMETERS:
27
33
  parameters.append(parameter)
28
34
  elif multiple_requests:
29
- parameter.has_multiple_media_types = True
30
- multiple_media_type_parameters.append(parameter)
35
+ parameter.has_multiple_content_types = True
36
+ multiple_content_type_parameters.append(parameter)
31
37
  else:
32
38
  parameters.append(parameter)
33
39
 
34
- if multiple_media_type_parameters:
40
+ if multiple_content_type_parameters:
35
41
  body_parameters_name_set = set(
36
- p.serialized_name for p in multiple_media_type_parameters
42
+ p.serialized_name for p in multiple_content_type_parameters
37
43
  )
38
44
  if len(body_parameters_name_set) > 1:
39
45
  raise ValueError(
@@ -53,7 +59,7 @@ def create_parameters(yaml_data: Dict[str, Any], code_model, parameter_creator:
53
59
  if parameter_original_id in parameters_index:
54
60
  parameter.original_parameter = parameters_index[parameter_original_id]
55
61
 
56
- return parameters, multiple_media_type_parameters
62
+ return parameters, multiple_content_type_parameters
57
63
 
58
64
  class BaseBuilder(BaseModel):
59
65
  """Base class for Operations and Request Builders"""
@@ -65,6 +71,7 @@ class BaseBuilder(BaseModel):
65
71
  name: str,
66
72
  description: str,
67
73
  parameters: "ParameterListType",
74
+ schema_requests: List[SchemaRequest],
68
75
  responses: Optional[List[SchemaResponse]] = None,
69
76
  summary: Optional[str] = None,
70
77
  ) -> None:
@@ -75,6 +82,7 @@ class BaseBuilder(BaseModel):
75
82
  self.parameters = parameters
76
83
  self.responses = responses or []
77
84
  self.summary = summary
85
+ self.schema_requests = schema_requests
78
86
 
79
87
  @property
80
88
  def default_content_type_declaration(self) -> str:
@@ -73,7 +73,9 @@ class Client:
73
73
  )
74
74
  file_import.add_from_import("azure.core.rest", "HttpRequest", ImportType.AZURECORE, TypingSection.CONDITIONAL)
75
75
  for og in self.code_model.operation_groups:
76
- file_import.add_from_import(".operations", og.class_name, ImportType.LOCAL)
76
+ file_import.add_from_import(
77
+ f".{self.code_model.operations_folder_name}", og.class_name, ImportType.LOCAL
78
+ )
77
79
 
78
80
  if self.code_model.sorted_schemas:
79
81
  path_to_models = ".." if async_mode else "."
@@ -94,6 +96,6 @@ class Client:
94
96
  pass
95
97
  return file_import
96
98
 
97
- def send_request_signature(self, async_mode) -> List[str]:
99
+ def send_request_signature(self, async_mode: bool, is_python3_file: bool) -> List[str]:
98
100
  request_signature = ["request: HttpRequest," if async_mode else "request, # type: HttpRequest"]
99
- return request_signature + self.parameters.method_signature_kwargs(async_mode)
101
+ return request_signature + self.parameters.method_signature_kwargs(is_python3_file)
@@ -29,7 +29,7 @@ from .rest import Rest
29
29
 
30
30
  _LOGGER = logging.getLogger(__name__)
31
31
 
32
- class CodeModel: # pylint: disable=too-many-instance-attributes
32
+ class CodeModel: # pylint: disable=too-many-instance-attributes, too-many-public-methods
33
33
  """Holds all of the information we have parsed out of the yaml file. The CodeModel is what gets
34
34
  serialized by the serializers.
35
35
 
@@ -203,6 +203,10 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
203
203
  operation for operation in operation_group.operations if operation not in next_operations
204
204
  ]
205
205
 
206
+ @property
207
+ def has_schemas(self):
208
+ return self.schemas or self.enums
209
+
206
210
  @property
207
211
  def default_authentication_policy(self) -> Type[CredentialSchemaPolicy]:
208
212
  return ARMChallengeAuthenticationPolicy if self.options['azure_arm'] else BearerTokenCredentialPolicy
@@ -238,6 +242,15 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
238
242
 
239
243
  return properties
240
244
 
245
+ @property
246
+ def operations_folder_name(self) -> str:
247
+ name = "operations"
248
+ if self.options["version_tolerant"] and not any(
249
+ og for og in self.operation_groups if not og.is_empty_operation_group
250
+ ):
251
+ name = f"_{name}"
252
+ return name
253
+
241
254
  def _add_properties_from_inheritance(self) -> None:
242
255
  """Adds properties from base classes to schemas with parents.
243
256
 
@@ -316,7 +329,7 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
316
329
  for operation in operation_group.operations:
317
330
  for obj in chain(
318
331
  operation.parameters,
319
- operation.multiple_media_type_parameters or [],
332
+ operation.multiple_content_type_parameters or [],
320
333
  operation.responses,
321
334
  operation.exceptions,
322
335
  chain.from_iterable(response.headers for response in operation.responses),
@@ -337,11 +350,11 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
337
350
  for parameter in self.global_parameters:
338
351
  self._populate_schema(parameter)
339
352
 
340
- def generate_single_parameter_from_multiple_media_types_operation(self) -> None:
353
+ def generate_single_parameter_from_multiple_content_types_operation(self) -> None:
341
354
  for operation_group in self.operation_groups:
342
355
  for operation in operation_group.operations:
343
- if operation.multiple_media_type_parameters:
344
- operation.convert_multiple_media_type_parameters()
356
+ if operation.multiple_content_type_parameters:
357
+ operation.convert_multiple_content_type_parameters()
345
358
 
346
359
  @property
347
360
  def need_vendored_code(self) -> bool:
@@ -383,3 +396,4 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
383
396
  if isinstance(operation, LROOperation):
384
397
  request_builder.name = request_builder.name + "_initial"
385
398
  operation.request_builder = request_builder
399
+ operation.link_body_kwargs_to_body_params()
@@ -36,6 +36,8 @@ class FileImport:
36
36
  TypingSection,
37
37
  Dict[ImportType, Dict[str, Set[Optional[Union[str, Tuple[str, str]]]]]]
38
38
  ] = imports or dict()
39
+ # has sync and async type definitions
40
+ self.type_definitions: Dict[str, Tuple[str, str]] = {}
39
41
 
40
42
  def _add_import(
41
43
  self,
@@ -82,6 +84,10 @@ class FileImport:
82
84
  ]:
83
85
  return self._imports
84
86
 
87
+ def define_mypy_type(self, type_name: str, type_value: str, async_type_value: Optional[str] = None):
88
+ self._add_import("typing", ImportType.STDLIB, "TypeVar", TypingSection.CONDITIONAL)
89
+ self.type_definitions[type_name] = (type_value, async_type_value or type_value)
90
+
85
91
  def merge(self, file_import: "FileImport") -> None:
86
92
  """Merge the given file import format."""
87
93
  for typing_section, import_type_dict in file_import.imports.items():
@@ -89,3 +95,4 @@ class FileImport:
89
95
  for package_name, module_list in package_list.items():
90
96
  for module_name in module_list:
91
97
  self._add_import(package_name, import_type, module_name, typing_section)
98
+ self.type_definitions.update(file_import.type_definitions)
@@ -11,6 +11,7 @@ from .parameter_list import ParameterList
11
11
  from .schema_response import SchemaResponse
12
12
  from .imports import ImportType, TypingSection
13
13
  from .base_schema import BaseSchema
14
+ from .schema_request import SchemaRequest
14
15
 
15
16
  _LOGGER = logging.getLogger(__name__)
16
17
 
@@ -24,7 +25,8 @@ class LROOperation(Operation):
24
25
  description: str,
25
26
  api_versions: Set[str],
26
27
  parameters: ParameterList,
27
- multiple_media_type_parameters: ParameterList,
28
+ multiple_content_type_parameters: ParameterList,
29
+ schema_requests: List[SchemaRequest],
28
30
  summary: Optional[str] = None,
29
31
  responses: Optional[List[SchemaResponse]] = None,
30
32
  exceptions: Optional[List[SchemaResponse]] = None,
@@ -38,7 +40,8 @@ class LROOperation(Operation):
38
40
  description,
39
41
  api_versions,
40
42
  parameters,
41
- multiple_media_type_parameters,
43
+ multiple_content_type_parameters,
44
+ schema_requests,
42
45
  summary,
43
46
  responses,
44
47
  exceptions,
@@ -86,7 +89,8 @@ class LROOperation(Operation):
86
89
  description="",
87
90
  api_versions=self.api_versions,
88
91
  parameters=self.parameters,
89
- multiple_media_type_parameters=self.multiple_media_type_parameters,
92
+ schema_requests=self.schema_requests,
93
+ multiple_content_type_parameters=self.multiple_content_type_parameters,
90
94
  summary=self.summary,
91
95
  responses=self.responses,
92
96
  want_description_docstring=False,
@@ -224,15 +224,15 @@ class HiddenModelObjectSchema(ObjectSchema):
224
224
 
225
225
  @property
226
226
  def type_annotation(self) -> str:
227
- return "Any"
227
+ return "JSONType"
228
228
 
229
229
  @property
230
230
  def operation_type_annotation(self) -> str:
231
- return "Any"
231
+ return "JSONType"
232
232
 
233
233
  @property
234
234
  def docstring_type(self) -> str:
235
- return "Any"
235
+ return "JSONType"
236
236
 
237
237
  @property
238
238
  def docstring_text(self) -> str:
@@ -10,11 +10,13 @@ from typing import cast, Dict, List, Any, Optional, Union, Set
10
10
  from .base_builder import BaseBuilder, create_parameters
11
11
  from .imports import FileImport, ImportType, TypingSection
12
12
  from .schema_response import SchemaResponse
13
- from .parameter import get_parameter
13
+ from .parameter import Parameter, get_parameter
14
14
  from .parameter_list import ParameterList, get_parameter_list
15
15
  from .base_schema import BaseSchema
16
16
  from .object_schema import ObjectSchema
17
17
  from .request_builder import RequestBuilder
18
+ from .schema_request import SchemaRequest
19
+ from .primitive_schemas import IOSchema
18
20
 
19
21
  _LOGGER = logging.getLogger(__name__)
20
22
 
@@ -30,7 +32,8 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
30
32
  description: str,
31
33
  api_versions: Set[str],
32
34
  parameters: ParameterList,
33
- multiple_media_type_parameters: ParameterList,
35
+ multiple_content_type_parameters: ParameterList,
36
+ schema_requests: List[SchemaRequest],
34
37
  summary: Optional[str] = None,
35
38
  responses: Optional[List[SchemaResponse]] = None,
36
39
  exceptions: Optional[List[SchemaResponse]] = None,
@@ -44,11 +47,12 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
44
47
  description=description,
45
48
  parameters=parameters,
46
49
  responses=responses,
50
+ schema_requests=schema_requests,
47
51
  summary=summary,
48
52
  )
49
- self.multiple_media_type_parameters = multiple_media_type_parameters
53
+ self.multiple_content_type_parameters = multiple_content_type_parameters
50
54
  self.api_versions = api_versions
51
- self.multiple_media_type_parameters = multiple_media_type_parameters
55
+ self.multiple_content_type_parameters = multiple_content_type_parameters
52
56
  self.exceptions = exceptions or []
53
57
  self.want_description_docstring = want_description_docstring
54
58
  self.want_tracing = want_tracing
@@ -79,28 +83,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
79
83
 
80
84
  @property
81
85
  def body_kwargs_to_pass_to_request_builder(self) -> List[str]:
82
- kwargs = []
83
- if not self.parameters.has_body:
84
- return []
85
- body_kwarg_names = self.request_builder.parameters.body_kwarg_names
86
- for kwarg in body_kwarg_names:
87
- if kwarg == "content":
88
- if self.request_builder.is_stream:
89
- kwargs.append("content")
90
- else:
91
- kwargs.append(kwarg)
92
- if not kwargs and not self.parameters.body[0].constant:
93
- kwargs.append("content")
94
- return kwargs
95
-
96
- @property
97
- def serialized_body_kwarg(self) -> str:
98
- # body serialization can be passed to either "json" or "content"
99
- if "json" in self.body_kwargs_to_pass_to_request_builder:
100
- return "json"
101
- if not self.request_builder.is_stream:
102
- return "content"
103
- raise ValueError("You should not be trying to serialize this body")
86
+ return [p.serialized_name for p in self.request_builder.body_kwargs_to_get]
104
87
 
105
88
  @property
106
89
  def has_optional_return_type(self) -> bool:
@@ -168,7 +151,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
168
151
  for param in self.parameters.method:
169
152
  file_import.merge(param.imports())
170
153
 
171
- for param in self.multiple_media_type_parameters:
154
+ for param in self.multiple_content_type_parameters:
172
155
  file_import.merge(param.imports())
173
156
 
174
157
  for response in self.responses:
@@ -239,30 +222,72 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
239
222
  file_import.add_from_import(
240
223
  f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
241
224
  )
225
+ if self.code_model.options["version_tolerant"] and (
226
+ self.parameters.has_body or
227
+ any(r for r in self.responses if r.has_body)
228
+ ):
229
+ file_import.define_mypy_type("JSONType", "Any")
242
230
  return file_import
243
231
 
244
- def convert_multiple_media_type_parameters(self) -> None:
232
+ def _get_body_param_from_body_kwarg(self, body_kwarg: Parameter) -> Parameter:
233
+ # determine which of the body parameters returned from m4 corresponds to this body_kwarg
234
+ if not self.multiple_content_type_parameters.has_body:
235
+ return self.parameters.body[0]
236
+ if body_kwarg.serialized_name == "data":
237
+ return next(p for p in self.multiple_content_type_parameters.body if p.is_data_input)
238
+ if body_kwarg.serialized_name == "files":
239
+ return next(p for p in self.multiple_content_type_parameters.body if p.is_multipart)
240
+ if body_kwarg.serialized_name == "json":
241
+ # first check if there's any non-binary. In the case of multiple content types, there's
242
+ # usually one binary (for content), and one schema parameter (for json)
243
+ try:
244
+ return next(
245
+ p for p in self.multiple_content_type_parameters.body
246
+ if not isinstance(p.schema, IOSchema)
247
+ )
248
+ except StopIteration:
249
+ return next(p for p in self.multiple_content_type_parameters.body if p.is_json_parameter)
250
+ return self.multiple_content_type_parameters.body[0]
251
+
252
+ def link_body_kwargs_to_body_params(self) -> None:
253
+ if not self.parameters.has_body:
254
+ return
255
+ body_kwargs = [
256
+ p for p in self.request_builder.parameters.body
257
+ if p.content_types
258
+ ]
259
+ if len(body_kwargs) == 1:
260
+ self.parameters.body[0].body_kwargs = [body_kwargs[0]]
261
+ return
262
+ for body_kwarg in body_kwargs:
263
+ body_param = self._get_body_param_from_body_kwarg(body_kwarg)
264
+ body_param.body_kwargs.append(body_kwarg)
265
+
266
+ def convert_multiple_content_type_parameters(self) -> None:
245
267
  type_annot = ", ".join([
246
268
  param.schema.operation_type_annotation
247
- for param in self.multiple_media_type_parameters
269
+ for param in self.multiple_content_type_parameters
248
270
  ])
249
271
  docstring_type = " or ".join([
250
- param.schema.docstring_type for param in self.multiple_media_type_parameters
272
+ param.schema.docstring_type for param in self.multiple_content_type_parameters
251
273
  ])
252
274
  try:
253
275
  # get an optional param with object first. These params are the top choice
254
276
  # bc they have more info about how to serialize the body
255
277
  chosen_parameter = next(
256
- p for p in self.multiple_media_type_parameters if not p.required and isinstance(p.schema, ObjectSchema)
278
+ p for p in self.multiple_content_type_parameters
279
+ if not p.required and isinstance(p.schema, ObjectSchema)
257
280
  )
258
281
  except StopIteration: # pylint: disable=broad-except
259
282
  # otherwise, we get the first optional param, if that exists. If not, we just grab the first one
260
- optional_parameters = [p for p in self.multiple_media_type_parameters if not p.required]
261
- chosen_parameter = optional_parameters[0] if optional_parameters else self.multiple_media_type_parameters[0]
283
+ optional_parameters = [p for p in self.multiple_content_type_parameters if not p.required]
284
+ chosen_parameter = (
285
+ optional_parameters[0] if optional_parameters else self.multiple_content_type_parameters[0]
286
+ )
262
287
  if not chosen_parameter:
263
288
  raise ValueError("You are missing a parameter that has multiple media types")
264
- chosen_parameter.multiple_media_types_type_annot = f"Union[{type_annot}]"
265
- chosen_parameter.multiple_media_types_docstring_type = docstring_type
289
+ chosen_parameter.multiple_content_types_type_annot = f"Union[{type_annot}]"
290
+ chosen_parameter.multiple_content_types_docstring_type = docstring_type
266
291
  self.parameters.append(chosen_parameter)
267
292
 
268
293
  @classmethod
@@ -272,7 +297,10 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
272
297
 
273
298
  parameter_creator = get_parameter(code_model).from_yaml
274
299
  parameter_list_creator = get_parameter_list(code_model)
275
- parameters, multiple_media_type_parameters = create_parameters(yaml_data, code_model, parameter_creator)
300
+ schema_requests = [SchemaRequest.from_yaml(yaml, code_model=code_model) for yaml in yaml_data["requests"]]
301
+ parameters, multiple_content_type_parameters = create_parameters(
302
+ yaml_data, code_model, parameter_creator
303
+ )
276
304
 
277
305
  return cls(
278
306
  code_model=code_model,
@@ -280,8 +308,11 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
280
308
  name=name,
281
309
  description=yaml_data["language"]["python"]["description"],
282
310
  api_versions=set(value_dict["version"] for value_dict in yaml_data["apiVersions"]),
283
- parameters=parameter_list_creator(code_model, parameters),
284
- multiple_media_type_parameters=parameter_list_creator(code_model, multiple_media_type_parameters),
311
+ parameters=parameter_list_creator(code_model, parameters, schema_requests),
312
+ multiple_content_type_parameters=parameter_list_creator(
313
+ code_model, multiple_content_type_parameters, schema_requests
314
+ ),
315
+ schema_requests=schema_requests,
285
316
  summary=yaml_data["language"]["python"].get("summary"),
286
317
  responses=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("responses", [])],
287
318
  # Exception with no schema means default exception, we don't store them
@@ -56,7 +56,7 @@ class OperationGroup(BaseModel):
56
56
  file_import.merge(operation.imports_for_multiapi(async_mode))
57
57
  return file_import
58
58
 
59
- def imports(self, async_mode: bool, has_schemas: bool) -> FileImport:
59
+ def imports(self, async_mode: bool) -> FileImport:
60
60
  file_import = FileImport()
61
61
  file_import.add_from_import("azure.core.exceptions", "ClientAuthenticationError", ImportType.AZURECORE)
62
62
  file_import.add_from_import("azure.core.exceptions", "ResourceNotFoundError", ImportType.AZURECORE)
@@ -73,7 +73,7 @@ class OperationGroup(BaseModel):
73
73
  "azure.core.tracing.decorator", "distributed_trace", ImportType.AZURECORE,
74
74
  )
75
75
  local_path = "..." if async_mode else ".."
76
- if has_schemas and self.code_model.options["models_mode"]:
76
+ if self.code_model.has_schemas and self.code_model.options["models_mode"]:
77
77
  file_import.add_from_import(local_path, "models", ImportType.LOCAL, alias="_models")
78
78
  if self.code_model.options["builders_visibility"] == "embedded" and async_mode:
79
79
  if not self.code_model.options["combine_operation_files"]:
@@ -85,25 +85,30 @@ class OperationGroup(BaseModel):
85
85
  else:
86
86
  operation_group_builders = self.code_model.rest.request_builders
87
87
  for request_builder in operation_group_builders:
88
+ python3_only = self.code_model.options["python3_only"]
89
+ typed_sync_operation_file = self.code_model.options["add_python3_operation_files"]
90
+ suffix = "_py3" if typed_sync_operation_file and not python3_only else ""
88
91
  file_import.add_from_import(
89
- f"...operations.{self.filename}",
92
+ f"...{self.code_model.operations_folder_name}.{self.filename}{suffix}",
90
93
  request_builder.name,
91
94
  import_type=ImportType.LOCAL
92
95
  )
93
-
96
+ type_value = "Optional[Callable[[PipelineResponse[HttpRequest, {}HttpResponse], T, Dict[str, Any]], Any]]"
97
+ file_import.define_mypy_type(
98
+ "ClsType",
99
+ type_value.format(""),
100
+ type_value.format("Async")
101
+ )
94
102
  return file_import
95
103
 
96
104
 
97
105
  @property
98
106
  def filename(self) -> str:
99
- if self.code_model.options["combine_operation_files"]:
100
- return "_operations"
101
-
102
107
  basename = self.name
103
108
  if self.is_empty_operation_group:
104
109
  basename = self.code_model.module_name
105
110
 
106
- if basename == "operations":
111
+ if basename == "operations" or self.code_model.options["combine_operation_files"]:
107
112
  return f"_operations"
108
113
  return f"_{basename}_operations"
109
114
 
@@ -11,6 +11,7 @@ from .schema_response import SchemaResponse
11
11
  from .request_builder import RequestBuilder
12
12
  from .imports import ImportType, FileImport, TypingSection
13
13
  from .object_schema import ObjectSchema
14
+ from .schema_request import SchemaRequest
14
15
  from .parameter_list import ParameterList
15
16
 
16
17
  _LOGGER = logging.getLogger(__name__)
@@ -25,7 +26,8 @@ class PagingOperation(Operation):
25
26
  description: str,
26
27
  api_versions: Set[str],
27
28
  parameters: ParameterList,
28
- multiple_media_type_parameters: ParameterList,
29
+ multiple_content_type_parameters: ParameterList,
30
+ schema_requests: List[SchemaRequest],
29
31
  summary: Optional[str] = None,
30
32
  responses: Optional[List[SchemaResponse]] = None,
31
33
  exceptions: Optional[List[SchemaResponse]] = None,
@@ -41,7 +43,8 @@ class PagingOperation(Operation):
41
43
  description,
42
44
  api_versions,
43
45
  parameters,
44
- multiple_media_type_parameters,
46
+ multiple_content_type_parameters,
47
+ schema_requests,
45
48
  summary,
46
49
  responses,
47
50
  exceptions,