@autorest/python 5.17.0 → 5.18.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.
package/ChangeLog.md CHANGED
@@ -1,4 +1,38 @@
1
- # Change Log
1
+ # Change
2
+
3
+ ### 2022-07-09 - 5.18.0
4
+
5
+ | Library | Min Version |
6
+ | ----------------------------------------------------------------------- | ----------- |
7
+ | `@autorest/core` | `3.8.1` |
8
+ | `@autorest/modelerfour` | `4.23.5` |
9
+ | `azure-core` dep of generated code | `1.23.0` |
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**
14
+
15
+ - No longer allow users to specify `api_version` on the method level #1281
16
+ - Make `content_type` param required with no default if streaming with no `application/octet-stream` #1288
17
+
18
+ **Bug Fixes**
19
+
20
+ - Fix duplicate params in signature with `--payload-flattening-threshold` #1289
21
+ - Fix overloaded request builder signatures #1289
22
+
23
+ ### 2022-xx-xx - 5.17.1
24
+
25
+ | Library | Min Version |
26
+ | ----------------------------------------------------------------------- | ----------- |
27
+ | `@autorest/core` | `3.8.1` |
28
+ | `@autorest/modelerfour` | `4.23.5` |
29
+ | `azure-core` dep of generated code | `1.23.0` |
30
+ | `msrest` dep of generated code | `0.6.21` |
31
+ | `azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0` |
32
+
33
+ **Bug Fixes**
34
+
35
+ - Improve docstring templates, specifically for polymorphic bodies #1279
2
36
 
3
37
  ### 2022-06-02 - 5.17.0
4
38
 
@@ -11,6 +11,7 @@ from .imports import FileImport
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from .code_model import CodeModel
14
+ from .model_type import ModelType
14
15
 
15
16
 
16
17
  class BaseType(BaseModel, ABC):
@@ -143,6 +144,11 @@ class BaseType(BaseModel, ABC):
143
144
  """Template of what this schema would look like as JSON input"""
144
145
  ...
145
146
 
147
+ def get_polymorphic_subtypes( # pylint: disable=no-self-use
148
+ self, polymorphic_subtypes: List["ModelType"] # pylint: disable=unused-argument
149
+ ) -> None:
150
+ return None
151
+
146
152
  @property
147
153
  @abstractmethod
148
154
  def instance_check_template(self) -> str:
@@ -3,12 +3,13 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from typing import Any, Dict, Optional, TYPE_CHECKING
6
+ from typing import Any, Dict, Optional, TYPE_CHECKING, List
7
7
  from .base_type import BaseType
8
8
  from .imports import FileImport, ImportType, TypingSection
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from .code_model import CodeModel
12
+ from .model_type import ModelType
12
13
 
13
14
 
14
15
  class DictionaryType(BaseType):
@@ -79,6 +80,20 @@ class DictionaryType(BaseType):
79
80
  )
80
81
  }
81
82
 
83
+ def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
84
+ from .model_type import ModelType
85
+
86
+ if isinstance(self.element_type, ModelType):
87
+ is_polymorphic_subtype = (
88
+ self.element_type.discriminator_value
89
+ and not self.element_type.discriminated_subtypes
90
+ )
91
+ if (
92
+ self.element_type.name not in (m.name for m in polymorphic_subtypes)
93
+ and is_polymorphic_subtype
94
+ ):
95
+ polymorphic_subtypes.append(self.element_type)
96
+
82
97
  @classmethod
83
98
  def from_yaml(
84
99
  cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
@@ -3,12 +3,13 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from typing import Any, Dict, Optional, Union, TYPE_CHECKING
6
+ from typing import Any, Dict, Optional, Union, TYPE_CHECKING, List
7
7
  from .base_type import BaseType
8
8
  from .imports import FileImport, ImportType, TypingSection
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from .code_model import CodeModel
12
+ from .model_type import ModelType
12
13
 
13
14
 
14
15
  class ListType(BaseType):
@@ -104,6 +105,20 @@ class ListType(BaseType):
104
105
  )
105
106
  ]
106
107
 
108
+ def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
109
+ from .model_type import ModelType
110
+
111
+ if isinstance(self.element_type, ModelType):
112
+ is_polymorphic_subtype = (
113
+ self.element_type.discriminator_value
114
+ and not self.element_type.discriminated_subtypes
115
+ )
116
+ if (
117
+ self.element_type.name not in (m.name for m in polymorphic_subtypes)
118
+ and is_polymorphic_subtype
119
+ ):
120
+ polymorphic_subtypes.append(self.element_type)
121
+
107
122
  @property
108
123
  def instance_check_template(self) -> str:
109
124
  return "isinstance({}, list)"
@@ -47,7 +47,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
47
47
  *,
48
48
  properties: Optional[List[Property]] = None,
49
49
  parents: Optional[List["ModelType"]] = None,
50
- discriminated_subtypes: Optional[Dict[str, str]] = None,
50
+ discriminated_subtypes: Optional[Dict[str, "ModelType"]] = None,
51
51
  ) -> None:
52
52
  super().__init__(yaml_data=yaml_data, code_model=code_model)
53
53
  self.name: str = self.yaml_data["name"]
@@ -61,6 +61,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
61
61
  )
62
62
  self._created_json_template_representation = False
63
63
  self.is_public: bool = self.yaml_data.get("isPublic", True)
64
+ self.snake_case_name: str = self.yaml_data["snakeCaseName"]
64
65
 
65
66
  @property
66
67
  def is_xml(self) -> bool:
@@ -111,6 +112,10 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
111
112
  # but we don't want to write a serialization context for an object.
112
113
  return super().xml_serialization_ctxt
113
114
 
115
+ @property
116
+ def discriminated_subtypes_name_mapping(self) -> Dict[str, str]:
117
+ return {k: v.name for k, v in self.discriminated_subtypes.items()}
118
+
114
119
  def get_json_template_representation(
115
120
  self,
116
121
  *,
@@ -121,6 +126,11 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
121
126
  if self._created_json_template_representation:
122
127
  return "..." # do this to avoid loop
123
128
  self._created_json_template_representation = True
129
+ if self.discriminated_subtypes:
130
+ # we will instead print the discriminated subtypes
131
+ self._created_json_template_representation = False
132
+ return self.snake_case_name
133
+
124
134
  # don't add additional properties, because there's not really a concept of
125
135
  # additional properties in the template
126
136
  representation = {
@@ -135,20 +145,30 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
135
145
  if not (p.is_discriminator or p.client_name == "additional_properties")
136
146
  ]
137
147
  }
138
- try:
139
- # add discriminator prop if there is one
140
- discriminator = next(p for p in self.properties if p.is_discriminator)
141
- representation[discriminator.rest_api_name] = (
142
- self.discriminator_value or discriminator.rest_api_name
143
- )
144
- except StopIteration:
145
- pass
148
+ if self.discriminator and self.discriminator_value:
149
+ representation[
150
+ f'"{self.discriminator.rest_api_name}"'
151
+ ] = f'"{self.discriminator_value}"'
146
152
 
147
153
  # once we've finished, we want to reset created_json_template_representation to false
148
154
  # so we can call it again
149
155
  self._created_json_template_representation = False
150
156
  return representation
151
157
 
158
+ def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
159
+ is_polymorphic_subtype = (
160
+ self.discriminator_value and not self.discriminated_subtypes
161
+ )
162
+ if (
163
+ self.name not in (m.name for m in polymorphic_subtypes)
164
+ and is_polymorphic_subtype
165
+ ):
166
+ polymorphic_subtypes.append(self)
167
+ for discriminated_subtype in self.discriminated_subtypes.values():
168
+ discriminated_subtype.get_polymorphic_subtypes(polymorphic_subtypes)
169
+ for property in self.properties:
170
+ property.get_polymorphic_subtypes(polymorphic_subtypes)
171
+
152
172
  @classmethod
153
173
  def from_yaml(
154
174
  cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
@@ -172,7 +192,10 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
172
192
  ]
173
193
  self.properties = _get_properties(self, properties)
174
194
  # checking to see if this is a polymorphic class
175
- self.discriminated_subtypes = self.yaml_data.get("discriminatedSubtypes", {})
195
+ self.discriminated_subtypes = {
196
+ k: cast(ModelType, build_type(v, code_model))
197
+ for k, v in self.yaml_data.get("discriminatedSubtypes", {}).items()
198
+ }
176
199
 
177
200
  @property
178
201
  def has_readonly_or_constant_property(self) -> bool:
@@ -416,10 +416,17 @@ class RequestBuilderParameterList(_RequestBuilderParameterList):
416
416
  class OverloadedRequestBuilderParameterList(_RequestBuilderParameterList):
417
417
  """Parameter list for OverloadedRequestBuilder"""
418
418
 
419
- def method_signature(self, is_python3_file: bool, async_mode: bool) -> List[str]:
420
- return self.method_signature_positional(
421
- is_python3_file, async_mode
422
- ) + self.method_signature_kwargs(is_python3_file)
419
+ def method_signature_keyword_only(
420
+ self, is_python3_file: bool, async_mode: bool
421
+ ) -> List[str]:
422
+ """Signature for keyword only parameters"""
423
+ if not (self.keyword_only and is_python3_file):
424
+ return []
425
+ return ["*,"] + [
426
+ parameter.method_signature(is_python3_file, async_mode)
427
+ for parameter in self.keyword_only
428
+ if parameter.location != ParameterLocation.BODY
429
+ ]
423
430
 
424
431
 
425
432
  class _ClientGlobalParameterList(
@@ -13,6 +13,7 @@ from .utils import add_to_description, add_to_pylint_disable
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from .code_model import CodeModel
16
+ from .model_type import ModelType
16
17
 
17
18
 
18
19
  class Property(BaseModel): # pylint: disable=too-many-instance-attributes
@@ -105,6 +106,19 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
105
106
  description=description,
106
107
  )
107
108
 
109
+ def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
110
+ from .model_type import ModelType
111
+
112
+ if isinstance(self.type, ModelType):
113
+ is_polymorphic_subtype = (
114
+ self.type.discriminator_value and not self.type.discriminated_subtypes
115
+ )
116
+ if (
117
+ self.type.name not in (m.name for m in polymorphic_subtypes)
118
+ and is_polymorphic_subtype
119
+ ):
120
+ polymorphic_subtypes.append(self.type)
121
+
108
122
  @property
109
123
  def validation(self) -> Optional[Dict[str, Any]]:
110
124
  retval: Dict[str, Any] = {}
@@ -64,7 +64,7 @@ class RequestBuilderBase(BaseBuilder[ParameterListType]):
64
64
  def response_docstring_text(self, **kwargs) -> str:
65
65
  return (
66
66
  "Returns an :class:`~azure.core.rest.HttpRequest` that you will pass to the client's "
67
- + "`send_request` method. See https://aka.ms/azsdk/python/protocol/quickstart for how to "
67
+ + "`send_request` method. See https://aka.ms/azsdk/dpcodegen/python/send_request for how to "
68
68
  + "incorporate this response into your code flow."
69
69
  )
70
70
 
@@ -30,6 +30,7 @@ from ..models import (
30
30
  OverloadedRequestBuilder,
31
31
  ConstantType,
32
32
  MultipartBodyParameter,
33
+ Property,
33
34
  RequestBuilderType,
34
35
  )
35
36
  from .parameter_serializer import ParameterSerializer, PopKwargType
@@ -83,6 +84,37 @@ def _json_dumps_template(template_representation: Any) -> Any:
83
84
  )
84
85
 
85
86
 
87
+ def _get_polymorphic_subtype_template(polymorphic_subtype: ModelType) -> List[str]:
88
+ retval: List[str] = []
89
+ retval.append("")
90
+ retval.append(
91
+ f'# JSON input template for discriminator value "{polymorphic_subtype.discriminator_value}":'
92
+ )
93
+ subtype_template = _json_dumps_template(
94
+ polymorphic_subtype.get_json_template_representation(),
95
+ )
96
+
97
+ def _get_polymorphic_parent(
98
+ polymorphic_subtype: Optional[ModelType],
99
+ ) -> Optional[ModelType]:
100
+ if not polymorphic_subtype:
101
+ return None
102
+ try:
103
+ return next(
104
+ p for p in polymorphic_subtype.parents if p.discriminated_subtypes
105
+ )
106
+ except StopIteration:
107
+ return None
108
+
109
+ polymorphic_parent = _get_polymorphic_parent(polymorphic_subtype)
110
+ while _get_polymorphic_parent(polymorphic_parent):
111
+ polymorphic_parent = _get_polymorphic_parent(polymorphic_parent)
112
+ retval.extend(
113
+ f"{cast(ModelType, polymorphic_parent).snake_case_name} = {subtype_template}".splitlines()
114
+ )
115
+ return retval
116
+
117
+
86
118
  def _serialize_grouped_body(builder: BuilderType) -> List[str]:
87
119
  retval: List[str] = []
88
120
  for grouped_parameter in builder.parameters.grouped:
@@ -268,6 +300,11 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
268
300
  return []
269
301
  return self.param_description(builder) + self.response_docstring(builder)
270
302
 
303
+ @property
304
+ @abstractmethod
305
+ def _json_response_template_name(self) -> str:
306
+ ...
307
+
271
308
  def _json_input_example_template(self, builder: BuilderType) -> List[str]:
272
309
  template: List[str] = []
273
310
  if self.code_model.options["models_mode"]:
@@ -287,14 +324,26 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
287
324
  if not isinstance(body_param.type, (ListType, DictionaryType, ModelType)):
288
325
  return template
289
326
 
290
- if isinstance(body_param.type, ModelType) and body_param.type.discriminator:
291
- discriminator_name = body_param.type.discriminator.client_name
292
- discriminator_values = body_param.type.discriminated_subtypes.keys()
327
+ polymorphic_subtypes: List[ModelType] = []
328
+ body_param.type.get_polymorphic_subtypes(polymorphic_subtypes)
329
+ if polymorphic_subtypes:
330
+ # we just assume one kind of polymorphic body for input
331
+ discriminator_name = cast(
332
+ Property, polymorphic_subtypes[0].discriminator
333
+ ).rest_api_name
293
334
  template.append(
294
- "{} = '{}'".format(
295
- discriminator_name, "' or '".join(discriminator_values)
296
- )
335
+ "# The input is polymorphic. The following are possible polymorphic "
336
+ f'inputs based off discriminator "{discriminator_name}":'
297
337
  )
338
+ for idx in range(
339
+ min(
340
+ self.code_model.options["polymorphic_examples"],
341
+ len(polymorphic_subtypes),
342
+ )
343
+ ):
344
+ template.extend(
345
+ _get_polymorphic_subtype_template(polymorphic_subtypes[idx])
346
+ )
298
347
  template.append("")
299
348
  template.append(
300
349
  "# JSON input template you can fill out and use as your body input."
@@ -337,7 +386,7 @@ class RequestBuilderSerializer(
337
386
  def description_and_summary(self, builder: RequestBuilderType) -> List[str]:
338
387
  retval = super().description_and_summary(builder)
339
388
  retval += [
340
- "See https://aka.ms/azsdk/python/protocol/quickstart for how to incorporate this "
389
+ "See https://aka.ms/azsdk/dpcodegen/python/send_request for how to incorporate this "
341
390
  "request builder into your code flow.",
342
391
  "",
343
392
  ]
@@ -351,6 +400,10 @@ class RequestBuilderSerializer(
351
400
  def serializer_name(self) -> str:
352
401
  return "_SERIALIZER"
353
402
 
403
+ @property
404
+ def _json_response_template_name(self) -> str:
405
+ return "response.json()"
406
+
354
407
  @staticmethod
355
408
  def declare_non_inputtable_constants(builder: RequestBuilderType) -> List[str]:
356
409
  def _get_value(param):
@@ -381,7 +434,7 @@ class RequestBuilderSerializer(
381
434
  def response_docstring(self, builder: RequestBuilderType) -> List[str]:
382
435
  response_str = (
383
436
  f":return: Returns an :class:`~azure.core.rest.HttpRequest` that you will pass to the client's "
384
- + "`send_request` method. See https://aka.ms/azsdk/python/protocol/quickstart for how to "
437
+ + "`send_request` method. See https://aka.ms/azsdk/dpcodegen/python/send_request for how to "
385
438
  + "incorporate this response into your code flow."
386
439
  )
387
440
  rtype_str = f":rtype: ~azure.core.rest.HttpRequest"
@@ -473,10 +526,38 @@ class _OperationSerializer(
473
526
  retval.append("")
474
527
  return retval
475
528
 
529
+ @property
530
+ def _json_response_template_name(self) -> str:
531
+ return "response"
532
+
476
533
  def example_template(self, builder: OperationType) -> List[str]:
477
534
  retval = super().example_template(builder)
478
535
  if self.code_model.options["models_mode"]:
479
536
  return retval
537
+ for response in builder.responses:
538
+ polymorphic_subtypes: List[ModelType] = []
539
+ if not response.type:
540
+ continue
541
+ response.type.get_polymorphic_subtypes(polymorphic_subtypes)
542
+ if polymorphic_subtypes:
543
+ # we just assume one kind of polymorphic body for input
544
+ discriminator_name = cast(
545
+ Property, polymorphic_subtypes[0].discriminator
546
+ ).rest_api_name
547
+ retval.append(
548
+ "# The response is polymorphic. The following are possible polymorphic "
549
+ f'responses based off discriminator "{discriminator_name}":'
550
+ )
551
+ for idx in range(
552
+ min(
553
+ self.code_model.options["polymorphic_examples"],
554
+ len(polymorphic_subtypes),
555
+ )
556
+ ):
557
+ retval.extend(
558
+ _get_polymorphic_subtype_template(polymorphic_subtypes[idx])
559
+ )
560
+
480
561
  if _get_json_response_template_to_status_codes(builder):
481
562
  retval.append("")
482
563
  for (
@@ -488,7 +569,9 @@ class _OperationSerializer(
488
569
  ", ".join(status_codes)
489
570
  )
490
571
  )
491
- retval.extend(f"response.json() == {response_body}".splitlines())
572
+ retval.extend(
573
+ f"{self._json_response_template_name} == {response_body}".splitlines()
574
+ )
492
575
  return retval
493
576
 
494
577
  def make_pipeline_call(self, builder: OperationType) -> List[str]:
@@ -616,6 +699,20 @@ class _OperationSerializer(
616
699
  body_kwarg_name = builder.request_builder.parameters.body_parameter.client_name
617
700
  if isinstance(body_param.type, BinaryType):
618
701
  retval.append(f"_{body_kwarg_name} = {body_param.client_name}")
702
+ if (
703
+ not body_param.default_content_type
704
+ and not next(
705
+ p for p in builder.parameters if p.rest_api_name == "Content-Type"
706
+ ).optional
707
+ ):
708
+ content_types = "'" + "', '".join(body_param.content_types) + "'"
709
+ retval.extend(
710
+ [
711
+ "if not content_type:",
712
+ f' raise TypeError("Missing required keyword-only argument: content_type. '
713
+ f'Known values are:" + "{content_types}")',
714
+ ]
715
+ )
619
716
  else:
620
717
  retval.extend(self._serialize_body_parameter(builder))
621
718
  return retval
@@ -224,7 +224,7 @@ class ClientSerializer:
224
224
  retval.extend(self._rest_request_example(async_mode))
225
225
  retval.append("")
226
226
  retval.append(
227
- "For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart"
227
+ "For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request"
228
228
  )
229
229
  retval.append(f"")
230
230
  retval.append(":param request: The network request you want to make. Required.")
@@ -127,7 +127,7 @@ class ModelBaseSerializer:
127
127
  def discriminator_docstring(model: ModelType) -> str:
128
128
  return (
129
129
  "You probably want to use the sub-classes and not this class directly. "
130
- f"Known sub-classes are: {', '.join(model.discriminated_subtypes.values())}"
130
+ f"Known sub-classes are: {', '.join(v.name for v in model.discriminated_subtypes.values())}"
131
131
  )
132
132
 
133
133
  @abstractmethod
@@ -3,6 +3,8 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
+
7
+
6
8
  def method_signature_and_response_type_annotation_template(
7
9
  *,
8
10
  is_python3_file: bool,
@@ -45,7 +45,7 @@
45
45
  {% if model.discriminated_subtypes %}
46
46
 
47
47
  _subtype_map = {
48
- '{{ model.discriminator.client_name }}': {{ str(model.discriminated_subtypes) }}
48
+ '{{ model.discriminator.client_name }}': {{ str(model.discriminated_subtypes_name_mapping) }}
49
49
  }
50
50
  {% endif %}
51
51
  {% if model.xml_map_content %}
@@ -134,7 +134,7 @@ def update_property(
134
134
 
135
135
  def update_discriminated_subtypes(yaml_data: Dict[str, Any]) -> Dict[str, Any]:
136
136
  return {
137
- obj["discriminatorValue"]: obj["language"]["default"]["name"]
137
+ obj["discriminatorValue"]: update_type(obj)
138
138
  for obj in yaml_data.get("discriminator", {}).get("immediate", {}).values()
139
139
  }
140
140
 
@@ -431,39 +431,6 @@ def update_client_url(yaml_data: Dict[str, Any]) -> str:
431
431
  ]["uri"]
432
432
 
433
433
 
434
- def update_content_type_parameter(
435
- yaml_data: Dict[str, Any],
436
- body_parameter: Optional[Dict[str, Any]],
437
- request_media_types: List[str],
438
- *,
439
- in_overload: bool = False,
440
- in_overriden: bool = False,
441
- ) -> Dict[str, Any]:
442
- # override content type type to string
443
- if not body_parameter:
444
- return yaml_data
445
- param = copy.deepcopy(yaml_data)
446
- param["schema"] = KNOWN_TYPES["string"] # override to string type
447
- param["required"] = False
448
- description = param["language"]["default"]["description"]
449
- if description and description[-1] != ".":
450
- description += "."
451
- if not (in_overriden or in_overload):
452
- param["inDocstring"] = False
453
- elif in_overload:
454
- description += (
455
- " Content type parameter for "
456
- f"{get_body_type_for_description(body_parameter)} body."
457
- )
458
- elif not in_overload:
459
- content_types = "'" + "', '".join(request_media_types) + "'"
460
- description += f" Known values are: {content_types}."
461
- if not in_overload and not in_overriden:
462
- param["clientDefaultValue"] = body_parameter["defaultContentType"]
463
- param["language"]["default"]["description"] = description
464
- return param
465
-
466
-
467
434
  class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-methods
468
435
  """Add Python naming information."""
469
436
 
@@ -471,11 +438,23 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
471
438
  def azure_arm(self) -> bool:
472
439
  return bool(self._autorestapi.get_boolean_value("azure-arm"))
473
440
 
441
+ @property
442
+ def version_tolerant(self) -> bool:
443
+ return bool(self._autorestapi.get_boolean_value("version-tolerant"))
444
+
445
+ @property
446
+ def low_level_client(self) -> bool:
447
+ return bool(self._autorestapi.get_boolean_value("low-level-client"))
448
+
449
+ @property
450
+ def legacy(self) -> bool:
451
+ return not (self.version_tolerant or self.low_level_client)
452
+
474
453
  @property
475
454
  def default_optional_constants_to_none(self) -> bool:
476
455
  return bool(
477
456
  self._autorestapi.get_boolean_value("default-optional-constants-to-none")
478
- or self._autorestapi.get_boolean_value("version-tolerant")
457
+ or self.version_tolerant
479
458
  )
480
459
 
481
460
  def update_overloads(
@@ -689,6 +668,9 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
689
668
  body_param["defaultContentType"] = _get_default_content_type(
690
669
  body_param["contentTypes"]
691
670
  )
671
+ # python supports IO input with all kinds of content_types
672
+ if body_type["type"] == "binary":
673
+ body_param["contentTypes"] = content_types or list(yaml_data.keys())
692
674
  if body_param["type"]["type"] == "constant":
693
675
  if not body_param["optional"] or (
694
676
  body_param["optional"] and not self.default_optional_constants_to_none
@@ -771,11 +753,53 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
771
753
  param["inFlattenedBody"] = True
772
754
  return param
773
755
 
756
+ def _update_content_type_parameter(
757
+ self,
758
+ yaml_data: Dict[str, Any],
759
+ body_parameter: Optional[Dict[str, Any]],
760
+ request_media_types: List[str],
761
+ *,
762
+ in_overload: bool = False,
763
+ in_overriden: bool = False,
764
+ ) -> Dict[str, Any]:
765
+ # override content type type to string
766
+ if not body_parameter:
767
+ return yaml_data
768
+ param = copy.deepcopy(yaml_data)
769
+ param["schema"] = KNOWN_TYPES["string"] # override to string type
770
+ if (
771
+ body_parameter["type"]["type"] == "binary"
772
+ and not body_parameter["defaultContentType"]
773
+ and not self.legacy
774
+ ):
775
+ param["required"] = True
776
+ else:
777
+ param["required"] = False
778
+ description = param["language"]["default"]["description"]
779
+ if description and description[-1] != ".":
780
+ description += "."
781
+ if not (in_overriden or in_overload):
782
+ param["inDocstring"] = False
783
+ elif in_overload:
784
+ description += (
785
+ " Content type parameter for "
786
+ f"{get_body_type_for_description(body_parameter)} body."
787
+ )
788
+ if not in_overload or (
789
+ body_parameter["type"]["type"] == "binary" and len(request_media_types) > 1
790
+ ):
791
+ content_types = "'" + "', '".join(request_media_types) + "'"
792
+ description += f" Known values are: {content_types}."
793
+ if not in_overload and not in_overriden:
794
+ param["clientDefaultValue"] = body_parameter["defaultContentType"]
795
+ param["language"]["default"]["description"] = description
796
+ return param
797
+
774
798
  def _update_parameters_helper(
775
799
  self,
776
800
  parameters: List[Dict[str, Any]],
777
801
  body_parameter: Optional[Dict[str, Any]],
778
- seen_rest_api_names: Set[str],
802
+ seen_client_names: Set[str],
779
803
  groupers: Dict[str, Dict[str, Any]],
780
804
  request_media_types: List[str],
781
805
  *,
@@ -785,15 +809,17 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
785
809
  retval: List[Dict[str, Any]] = []
786
810
  has_flattened_body = body_parameter and body_parameter.get("flattened")
787
811
  for param in parameters:
788
- serialized_name = param["language"]["default"].get("serializedName")
812
+ client_name = param["language"]["default"]["name"]
789
813
  if param["language"]["default"]["name"] == "$host" or (
790
- serialized_name and serialized_name in seen_rest_api_names
814
+ client_name in seen_client_names
791
815
  ):
792
816
  continue
817
+ seen_client_names.add(client_name)
793
818
  if param.get("origin") == "modelerfour:synthesized/api-version":
794
819
  param["inDocstring"] = False
795
- param["implementation"] = "Method"
796
- param["checkClientInput"] = True
820
+ if self.legacy:
821
+ param["implementation"] = "Method"
822
+ param["checkClientInput"] = True
797
823
  if has_flattened_body and param.get("targetProperty"):
798
824
  retval.append(self.update_flattened_parameter(param, body_parameter))
799
825
  continue
@@ -806,8 +832,8 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
806
832
  continue
807
833
  if is_body(param):
808
834
  continue
809
- if serialized_name == "Content-Type":
810
- param = update_content_type_parameter(
835
+ if param["language"]["default"].get("serializedName") == "Content-Type":
836
+ param = self._update_content_type_parameter(
811
837
  param,
812
838
  body_parameter,
813
839
  request_media_types,
@@ -818,7 +844,6 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
818
844
  param, in_overload=in_overload, in_overriden=in_overriden
819
845
  )
820
846
  retval.append(updated_param)
821
- seen_rest_api_names.add(updated_param["restApiName"])
822
847
  return retval
823
848
 
824
849
  def update_parameters(
@@ -830,7 +855,7 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
830
855
  in_overriden: bool = False,
831
856
  ) -> List[Dict[str, Any]]:
832
857
  retval: List[Dict[str, Any]] = []
833
- seen_rest_api_names: Set[str] = set()
858
+ seen_client_names: Set[str] = set()
834
859
  groupers: Dict[str, Dict[str, Any]] = {}
835
860
  # first update top level parameters
836
861
  request_media_types = yaml_data.get("requestMediaTypes", [])
@@ -838,7 +863,7 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
838
863
  self._update_parameters_helper(
839
864
  yaml_data["parameters"],
840
865
  body_parameter,
841
- seen_rest_api_names,
866
+ seen_client_names,
842
867
  groupers,
843
868
  request_media_types,
844
869
  in_overload=in_overload,
@@ -857,7 +882,7 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
857
882
  self._update_parameters_helper(
858
883
  request.get("parameters", []),
859
884
  body_parameter,
860
- seen_rest_api_names,
885
+ seen_client_names,
861
886
  groupers,
862
887
  request_media_types,
863
888
  in_overload=in_overload,
@@ -921,15 +946,8 @@ class M4Reformatter(YamlUpdatePlugin): # pylint: disable=too-many-public-method
921
946
  name = global_parameter["language"]["default"]["name"]
922
947
  if name == "$host":
923
948
  # I am the non-parameterized endpoint. Modify name based off of flag
924
- version_tolerant = self._autorestapi.get_boolean_value(
925
- "version-tolerant", False
926
- )
927
- low_level_client = self._autorestapi.get_boolean_value(
928
- "low-level-client", False
929
- )
930
- client_name = (
931
- "endpoint" if (version_tolerant or low_level_client) else "base_url"
932
- )
949
+
950
+ client_name = "base_url" if self.legacy else "endpoint"
933
951
  global_parameter["language"]["default"]["description"] = "Service URL."
934
952
  global_params.append(
935
953
  self.update_parameter(
@@ -62,6 +62,7 @@ def update_types(yaml_data: List[Dict[str, Any]]) -> None:
62
62
  add_redefined_builtin_info(property["clientName"], property)
63
63
  if type.get("name"):
64
64
  type["description"] = update_description(type["description"], type["name"])
65
+ type["snakeCaseName"] = to_snake_case(type["name"])
65
66
 
66
67
 
67
68
  def update_client(yaml_data: Dict[str, Any]) -> None:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "5.17.0",
3
+ "version": "5.18.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "scripts": {
6
6
  "prepare": "node run-python3.js prepare.py",
@@ -27,7 +27,7 @@
27
27
  "@autorest/system-requirements": "~1.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@microsoft.azure/autorest.testserver": "^3.3.26"
30
+ "@microsoft.azure/autorest.testserver": "^3.3.28"
31
31
  },
32
32
  "files": [
33
33
  "autorest/**/*.py",