Skip to content
This repository was archived by the owner on May 22, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
**Bug Fixes**

- Add default value consistently for parameters #1164
- Make `content_type` param keyword-only if there are multiple content types #1167

### 2022-02-09 - 5.12.6

Expand Down
15 changes: 11 additions & 4 deletions autorest/codegen/models/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,17 +303,24 @@ def from_yaml(cls, yaml_data: Dict[str, Any], *, code_model) -> "Operation":
parameters, multiple_content_type_parameters = create_parameters(
yaml_data, code_model, parameter_creator
)
parameter_list = parameter_list_creator(code_model, parameters, schema_requests)
multiple_content_type_parameter_list = parameter_list_creator(
code_model, multiple_content_type_parameters, schema_requests
)

if len(parameter_list.content_types) > 1:
for p in parameter_list.parameters:
if p.rest_api_name == "Content-Type":
p.is_keyword_only = True

return cls(
code_model=code_model,
yaml_data=yaml_data,
name=name,
description=yaml_data["language"]["python"]["description"],
api_versions=set(value_dict["version"] for value_dict in yaml_data["apiVersions"]),
parameters=parameter_list_creator(code_model, parameters, schema_requests),
multiple_content_type_parameters=parameter_list_creator(
code_model, multiple_content_type_parameters, schema_requests
),
parameters=parameter_list,
multiple_content_type_parameters=multiple_content_type_parameter_list,
schema_requests=schema_requests,
summary=yaml_data["language"]["python"].get("summary"),
responses=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("responses", [])],
Expand Down
24 changes: 16 additions & 8 deletions autorest/codegen/models/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(
grouped_by: Optional["Parameter"] = None,
original_parameter: Optional["Parameter"] = None,
client_default_value: Optional[Any] = None,
keyword_only: bool = False,
keyword_only: Optional[bool] = None,
content_types: Optional[List[str]] = None,
) -> None:
super().__init__(yaml_data)
Expand Down Expand Up @@ -99,6 +99,7 @@ def __init__(
self.body_kwargs: List[Parameter] = []
self.is_body_kwarg = False
self.need_import = True
self.is_kwarg = (self.rest_api_name == "Content-Type" or (self.constant and self.rest_api_name != "Accept"))

def __hash__(self) -> int:
return hash(self.serialized_name)
Expand Down Expand Up @@ -302,19 +303,19 @@ def full_serialized_name(self) -> str:
origin_name = f"self._config.{self.serialized_name}"
return origin_name

@property
def is_kwarg(self) -> bool:
# this means "am I in **kwargs?"
return self.rest_api_name == "Content-Type" or (self.constant and self.rest_api_name != "Accept")

@property
def is_keyword_only(self) -> bool:
# this means in async mode, I am documented like def hello(positional_1, *, me!)
return self._keyword_only
return self._keyword_only or False

@is_keyword_only.setter
def is_keyword_only(self, val: bool) -> None:
self._keyword_only = val
self.is_kwarg = False

@property
def is_hidden(self) -> bool:
return self.serialized_name in _HIDDEN_KWARGS or (
return self.serialized_name in _HIDDEN_KWARGS and self.is_kwarg or (
self.yaml_data["implementation"] == "Client" and self.constant
)

Expand Down Expand Up @@ -369,6 +370,8 @@ class ParameterOnlyPathAndBodyPositional(Parameter):

@property
def is_keyword_only(self) -> bool:
if self._keyword_only is not None:
return self._keyword_only
return self.in_method_signature and not (
self.is_hidden or
self.location == ParameterLocation.Path or
Expand All @@ -377,6 +380,11 @@ def is_keyword_only(self) -> bool:
self.is_kwarg
)

@is_keyword_only.setter
def is_keyword_only(self, val: bool) -> None:
self._keyword_only = val
self.is_kwarg = False

def get_parameter(code_model):
if code_model.options["only_path_and_body_params_positional"]:
return ParameterOnlyPathAndBodyPositional
Expand Down
12 changes: 9 additions & 3 deletions autorest/codegen/models/parameter_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ def method(self) -> List[Parameter]:
lambda parameter: parameter.implementation == self.implementation
)
positional = [p for p in parameters_of_this_implementation if p.is_positional]
keyword_only = [p for p in parameters_of_this_implementation if p.is_keyword_only]
keyword_only = self._filter_out_multiple_content_type(
[p for p in parameters_of_this_implementation if p.is_keyword_only]
)
kwargs = self._filter_out_multiple_content_type(
[p for p in parameters_of_this_implementation if p.is_kwarg]
)
Expand Down Expand Up @@ -315,7 +317,9 @@ def method(self) -> List[Parameter]:
file_and_data_params.append(data_param)
method_params = [p for p in method_params if not p.is_multipart and not p.is_data_input]
positional = [p for p in method_params if p.is_positional]
keyword_only = [p for p in method_params if p.is_keyword_only]
keyword_only = self._filter_out_multiple_content_type(
[p for p in method_params if p.is_keyword_only]
)
kwargs = self._filter_out_multiple_content_type(
[p for p in method_params if p.is_kwarg]
)
Expand All @@ -338,7 +342,9 @@ def method(self) -> List[Parameter]:
"""
# Client level should not be on Method, etc.
positional = [p for p in self.parameters if p.is_positional]
keyword_only = [p for p in self.parameters if p.is_keyword_only]
keyword_only = self._filter_out_multiple_content_type(
[p for p in self.parameters if p.is_keyword_only]
)
kwargs = self._filter_out_multiple_content_type(
[p for p in self.parameters if p.is_kwarg]
)
Expand Down
5 changes: 5 additions & 0 deletions autorest/codegen/models/request_builder_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ def default_value_declaration(self) -> Optional[str]:
def is_keyword_only(self) -> bool:
return not self.location == ParameterLocation.Path and not self.is_kwarg

@is_keyword_only.setter
def is_keyword_only(self, val: bool) -> None:
self._keyword_only = val
self.is_kwarg = False

@property
def full_serialized_name(self) -> str:
return self.serialized_name
Expand Down
32 changes: 21 additions & 11 deletions autorest/codegen/serializers/builder_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,21 @@ def _serialize_flattened_body(builder) -> List[str]:
return retval

def _content_type_docstring(builder) -> str:
content_type_str = (
":keyword str content_type: Media type of the body sent to the API. " +
f'Default value is "{builder.parameters.default_content_type}". ' +
'Allowed values are: "{}."'.format('", "'.join(builder.parameters.content_types))
content_types = [f'"{c}"' for c in builder.parameters.content_types]
if len(content_types) == 2:
possible_values_str = " or ".join(content_types)
else:
possible_values_str = ", ".join(
content_types[: len(content_types) - 1]
) + f", and {content_types[-1]}"
default_value = next(
p for p in builder.parameters.method if p.rest_api_name == "Content-Type"
).default_value_declaration
return (
":keyword content_type: Media type of the body sent to the API. " +
f"Possible values are: {possible_values_str}. " +
f"Default value is {default_value}."
)
return content_type_str

class _BuilderSerializerProtocol(ABC):
@property
Expand Down Expand Up @@ -304,13 +313,14 @@ def param_description(self, builder: Union[RequestBuilder, Operation]) -> List[s
description_list.append(
f":{param.docstring_type_keyword} { param.serialized_name }: { param.docstring_type }"
)
try:
request_builder: RequestBuilder = cast(Operation, builder).request_builder
except AttributeError:
request_builder = cast(RequestBuilder, builder)

if len(request_builder.schema_requests) > 1:
description_list.append(_content_type_docstring(builder))
if len(builder.parameters.content_types) > 1:
description_list = [
_content_type_docstring(builder) if l.startswith(":keyword content_type:") else l
for l in description_list
]
if not any(l for l in description_list if l.startswith(":keyword content_type:")):
description_list.append(_content_type_docstring(builder))
return description_list

def param_description_and_response_docstring(self, builder) -> List[str]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,18 @@ def __init__(self, client, config, serializer, deserializer) -> None:
async def test_four( # pylint: disable=inconsistent-return-statements
self,
input: Optional[Union[IO, "_models.SourcePath"]] = None,
*,
content_type: Optional[Union[str, "_models.ContentType"]] = "application/json",
**kwargs: Any
) -> None:
"""TestFour should be in OperationGroupTwoOperations.

:param input: Input parameter. Default value is None.
:type input: IO or ~azure.multiapi.sample.v3.models.SourcePath
:keyword str content_type: Media type of the body sent to the API. Default value is
"application/json". Allowed values are: "application/pdf", "image/jpeg", "image/png",
"image/tiff", "application/json."
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/pdf", "image/jpeg", "image/png", "image/tiff", and "application/json". Default
value is "application/json".
:paramtype content_type: str or ~azure.multiapi.sample.v3.models.ContentType
:keyword callable cls: A custom type or function that will be passed the direct response
:return: None, or the result of cls(response)
:rtype: None
Expand All @@ -68,7 +71,6 @@ async def test_four( # pylint: disable=inconsistent-return-statements
error_map.update(kwargs.pop('error_map', {}))

api_version = kwargs.pop('api_version', "3.0.0") # type: str
content_type = kwargs.pop('content_type', "application/json") # type: Optional[Union[str, "_models.ContentType"]]

_json = None
_content = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,10 @@ def test_four( # pylint: disable=inconsistent-return-statements

:param input: Input parameter. Default value is None.
:type input: IO or ~azure.multiapi.sample.v3.models.SourcePath
:keyword str content_type: Media type of the body sent to the API. Default value is
"application/json". Allowed values are: "application/pdf", "image/jpeg", "image/png",
"image/tiff", "application/json."
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/pdf", "image/jpeg", "image/png", "image/tiff", and "application/json". Default
value is "application/json".
:paramtype content_type: str or ~azure.multiapi.sample.v3.models.ContentType
:keyword callable cls: A custom type or function that will be passed the direct response
:return: None, or the result of cls(response)
:rtype: None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,21 @@ def __init__(self, client, config, serializer, deserializer) -> None:

@distributed_trace_async
async def check_name_availability(
self, account_name: "_models.StorageAccountCheckNameAvailabilityParameters", **kwargs: Any
self,
account_name: "_models.StorageAccountCheckNameAvailabilityParameters",
*,
content_type: Optional[str] = "application/json",
**kwargs: Any
) -> "_models.CheckNameAvailabilityResult":
"""Checks that account name is valid and is not in use.

:param account_name: The name of the storage account within the specified resource group.
Storage account names must be between 3 and 24 characters in length and use numbers and
lower-case letters only.
:type account_name: ~storage.models.StorageAccountCheckNameAvailabilityParameters
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/json" or "text/json". Default value is "application/json".
:paramtype content_type: str
:keyword callable cls: A custom type or function that will be passed the direct response
:return: CheckNameAvailabilityResult, or the result of cls(response)
:rtype: ~storage.models.CheckNameAvailabilityResult
Expand All @@ -85,7 +92,6 @@ async def check_name_availability(
error_map.update(kwargs.pop("error_map", {}))

api_version = kwargs.pop("api_version", "2015-05-01-preview") # type: str
content_type = kwargs.pop("content_type", "application/json") # type: Optional[str]

_json = self._serialize.body(account_name, "StorageAccountCheckNameAvailabilityParameters")

Expand Down Expand Up @@ -122,14 +128,15 @@ async def _create_initial(
resource_group_name: str,
account_name: str,
parameters: "_models.StorageAccountCreateParameters",
*,
content_type: Optional[str] = "application/json",
**kwargs: Any
) -> Optional["_models.StorageAccount"]:
cls = kwargs.pop("cls", None) # type: ClsType[Optional["_models.StorageAccount"]]
error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError}
error_map.update(kwargs.pop("error_map", {}))

api_version = kwargs.pop("api_version", "2015-05-01-preview") # type: str
content_type = kwargs.pop("content_type", "application/json") # type: Optional[str]

_json = self._serialize.body(parameters, "StorageAccountCreateParameters")

Expand Down Expand Up @@ -171,6 +178,8 @@ async def begin_create(
resource_group_name: str,
account_name: str,
parameters: "_models.StorageAccountCreateParameters",
*,
content_type: Optional[str] = "application/json",
**kwargs: Any
) -> AsyncLROPoller["_models.StorageAccount"]:
"""Asynchronously creates a new storage account with the specified parameters. Existing accounts
Expand All @@ -186,6 +195,9 @@ async def begin_create(
:type account_name: str
:param parameters: The parameters to provide for the created account.
:type parameters: ~storage.models.StorageAccountCreateParameters
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/json" or "text/json". Default value is "application/json".
:paramtype content_type: str
:keyword callable cls: A custom type or function that will be passed the direct response
:keyword str continuation_token: A continuation token to restart a poller from a saved state.
:keyword polling: By default, your polling method will be AsyncARMPolling. Pass in False for
Expand All @@ -200,7 +212,6 @@ async def begin_create(
:raises: ~azure.core.exceptions.HttpResponseError
"""
api_version = kwargs.pop("api_version", "2015-05-01-preview") # type: str
content_type = kwargs.pop("content_type", "application/json") # type: Optional[str]
polling = kwargs.pop("polling", True) # type: Union[bool, AsyncPollingMethod]
cls = kwargs.pop("cls", None) # type: ClsType["_models.StorageAccount"]
lro_delay = kwargs.pop("polling_interval", self._config.polling_interval)
Expand All @@ -210,8 +221,8 @@ async def begin_create(
resource_group_name=resource_group_name,
account_name=account_name,
parameters=parameters,
api_version=api_version,
content_type=content_type,
api_version=api_version,
cls=lambda x, y, z: x,
**kwargs
)
Expand Down Expand Up @@ -347,6 +358,8 @@ async def update(
resource_group_name: str,
account_name: str,
parameters: "_models.StorageAccountUpdateParameters",
*,
content_type: Optional[str] = "application/json",
**kwargs: Any
) -> "_models.StorageAccount":
"""Updates the account type or tags for a storage account. It can also be used to add a custom
Expand All @@ -366,6 +379,9 @@ async def update(
:param parameters: The parameters to update on the account. Note that only one property can be
changed at a time using this API.
:type parameters: ~storage.models.StorageAccountUpdateParameters
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/json" or "text/json". Default value is "application/json".
:paramtype content_type: str
:keyword callable cls: A custom type or function that will be passed the direct response
:return: StorageAccount, or the result of cls(response)
:rtype: ~storage.models.StorageAccount
Expand All @@ -376,7 +392,6 @@ async def update(
error_map.update(kwargs.pop("error_map", {}))

api_version = kwargs.pop("api_version", "2015-05-01-preview") # type: str
content_type = kwargs.pop("content_type", "application/json") # type: Optional[str]

_json = self._serialize.body(parameters, "StorageAccountUpdateParameters")

Expand Down Expand Up @@ -601,6 +616,8 @@ async def regenerate_key(
resource_group_name: str,
account_name: str,
key_name: Optional[Union[str, "_models.KeyName"]] = None,
*,
content_type: Optional[str] = "application/json",
**kwargs: Any
) -> "_models.StorageAccountKeys":
"""Regenerates the access keys for the specified storage account.
Expand All @@ -613,6 +630,9 @@ async def regenerate_key(
:type account_name: str
:param key_name: Default value is None.
:type key_name: str or ~storage.models.KeyName
:keyword content_type: Media type of the body sent to the API. Possible values are:
"application/json" or "text/json". Default value is "application/json".
:paramtype content_type: str
:keyword callable cls: A custom type or function that will be passed the direct response
:return: StorageAccountKeys, or the result of cls(response)
:rtype: ~storage.models.StorageAccountKeys
Expand All @@ -623,7 +643,6 @@ async def regenerate_key(
error_map.update(kwargs.pop("error_map", {}))

api_version = kwargs.pop("api_version", "2015-05-01-preview") # type: str
content_type = kwargs.pop("content_type", "application/json") # type: Optional[str]

_regenerate_key = _models.StorageAccountRegenerateKeyParameters(key_name=key_name)
_json = self._serialize.body(_regenerate_key, "StorageAccountRegenerateKeyParameters")
Expand Down
Loading