@autorest/python 5.12.5 → 5.14.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 (41) hide show
  1. package/ChangeLog.md +185 -118
  2. package/autorest/black/__init__.py +3 -0
  3. package/autorest/codegen/__init__.py +34 -2
  4. package/autorest/codegen/models/__init__.py +2 -1
  5. package/autorest/codegen/models/client.py +6 -5
  6. package/autorest/codegen/models/code_model.py +3 -0
  7. package/autorest/codegen/models/constant_schema.py +0 -4
  8. package/autorest/codegen/models/lro_operation.py +6 -2
  9. package/autorest/codegen/models/operation.py +20 -11
  10. package/autorest/codegen/models/operation_group.py +0 -9
  11. package/autorest/codegen/models/paging_operation.py +3 -3
  12. package/autorest/codegen/models/parameter.py +35 -9
  13. package/autorest/codegen/models/parameter_list.py +11 -4
  14. package/autorest/codegen/models/request_builder.py +3 -2
  15. package/autorest/codegen/models/request_builder_parameter.py +5 -0
  16. package/autorest/codegen/models/request_builder_parameter_list.py +1 -0
  17. package/autorest/codegen/serializers/__init__.py +147 -74
  18. package/autorest/codegen/serializers/builder_serializer.py +68 -37
  19. package/autorest/codegen/serializers/client_serializer.py +14 -3
  20. package/autorest/codegen/serializers/general_serializer.py +4 -1
  21. package/autorest/codegen/serializers/utils.py +5 -1
  22. package/autorest/codegen/templates/CHANGELOG.md.jinja2 +6 -0
  23. package/autorest/codegen/templates/LICENSE.jinja2 +21 -0
  24. package/autorest/codegen/templates/MANIFEST.in.jinja2 +7 -0
  25. package/autorest/codegen/templates/README.md.jinja2 +105 -0
  26. package/autorest/codegen/templates/config.py.jinja2 +2 -7
  27. package/autorest/codegen/templates/dev_requirements.txt.jinja2 +10 -0
  28. package/autorest/codegen/templates/lro_operation.py.jinja2 +3 -1
  29. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +2 -0
  30. package/autorest/codegen/templates/operation.py.jinja2 +6 -2
  31. package/autorest/codegen/templates/operation_group.py.jinja2 +15 -18
  32. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -0
  33. package/autorest/codegen/templates/operation_tools.jinja2 +3 -2
  34. package/autorest/codegen/templates/paging_operation.py.jinja2 +2 -2
  35. package/autorest/codegen/templates/request_builder.py.jinja2 +2 -7
  36. package/autorest/codegen/templates/service_client.py.jinja2 +1 -1
  37. package/autorest/codegen/templates/setup.py.jinja2 +79 -20
  38. package/autorest/namer/name_converter.py +1 -1
  39. package/package.json +2 -2
  40. package/run-python3.js +1 -7
  41. package/venvtools.py +2 -2
@@ -159,7 +159,8 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
159
159
  if response.has_body:
160
160
  file_import.merge(cast(BaseSchema, response.schema).imports())
161
161
 
162
- if len([r for r in self.responses if r.has_body]) > 1:
162
+ response_types = [r.operation_type_annotation for r in self.responses if r.has_body]
163
+ if len(set(response_types)) > 1:
163
164
  file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
164
165
 
165
166
  if self.is_stream_response:
@@ -179,13 +180,10 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
179
180
  file_import.add_submodule_import("azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.AZURECORE)
180
181
  file_import.add_submodule_import("azure.core.exceptions", "HttpResponseError", ImportType.AZURECORE)
181
182
 
182
-
183
- file_import.add_import("functools", ImportType.STDLIB)
184
183
  file_import.add_submodule_import("typing", "Callable", ImportType.STDLIB, TypingSection.CONDITIONAL)
185
184
  file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
186
185
  file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB, TypingSection.CONDITIONAL)
187
186
  file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB, TypingSection.CONDITIONAL)
188
- file_import.add_submodule_import("typing", "Generic", ImportType.STDLIB, TypingSection.CONDITIONAL)
189
187
  file_import.add_submodule_import("azure.core.pipeline", "PipelineResponse", ImportType.AZURECORE)
190
188
  file_import.add_submodule_import("azure.core.rest", "HttpRequest", ImportType.AZURECORE)
191
189
  if async_mode:
@@ -193,9 +191,7 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
193
191
  else:
194
192
  file_import.add_submodule_import("azure.core.pipeline.transport", "HttpResponse", ImportType.AZURECORE)
195
193
 
196
- # Deprecation
197
- # FIXME: Replace with "the YAML contains deprecated:true"
198
- if True: # pylint: disable=using-constant-test
194
+ if self.deprecated:
199
195
  file_import.add_import("warnings", ImportType.STDLIB)
200
196
 
201
197
  if self.code_model.options["builders_visibility"] != "embedded":
@@ -227,6 +223,12 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
227
223
  any(r for r in self.responses if r.has_body)
228
224
  ):
229
225
  file_import.define_mypy_type("JSONType", "Any")
226
+ if self.code_model.options["tracing"] and self.want_tracing:
227
+ file_import.add_submodule_import(
228
+ f"azure.core.tracing.decorator{'_async' if async_mode else ''}",
229
+ f"distributed_trace{'_async' if async_mode else ''}",
230
+ ImportType.AZURECORE,
231
+ )
230
232
  return file_import
231
233
 
232
234
  def _get_body_param_from_body_kwarg(self, body_kwarg: Parameter) -> Parameter:
@@ -301,6 +303,15 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
301
303
  parameters, multiple_content_type_parameters = create_parameters(
302
304
  yaml_data, code_model, parameter_creator
303
305
  )
306
+ parameter_list = parameter_list_creator(code_model, parameters, schema_requests)
307
+ multiple_content_type_parameter_list = parameter_list_creator(
308
+ code_model, multiple_content_type_parameters, schema_requests
309
+ )
310
+
311
+ if len(parameter_list.content_types) > 1:
312
+ for p in parameter_list.parameters:
313
+ if p.rest_api_name == "Content-Type":
314
+ p.is_keyword_only = True
304
315
 
305
316
  return cls(
306
317
  code_model=code_model,
@@ -308,10 +319,8 @@ class Operation(BaseBuilder): # pylint: disable=too-many-public-methods, too-ma
308
319
  name=name,
309
320
  description=yaml_data["language"]["python"]["description"],
310
321
  api_versions=set(value_dict["version"] for value_dict in yaml_data["apiVersions"]),
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
- ),
322
+ parameters=parameter_list,
323
+ multiple_content_type_parameters=multiple_content_type_parameter_list,
315
324
  schema_requests=schema_requests,
316
325
  summary=yaml_data["language"]["python"].get("summary"),
317
326
  responses=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("responses", [])],
@@ -63,15 +63,6 @@ class OperationGroup(BaseModel):
63
63
  file_import.add_submodule_import("azure.core.exceptions", "ResourceExistsError", ImportType.AZURECORE)
64
64
  for operation in self.operations:
65
65
  file_import.merge(operation.imports(async_mode))
66
- if self.code_model.options["tracing"]:
67
- if async_mode:
68
- file_import.add_submodule_import(
69
- "azure.core.tracing.decorator_async", "distributed_trace_async", ImportType.AZURECORE,
70
- )
71
- else:
72
- file_import.add_submodule_import(
73
- "azure.core.tracing.decorator", "distributed_trace", ImportType.AZURECORE,
74
- )
75
66
  local_path = "..." if async_mode else ".."
76
67
  if self.code_model.has_schemas and self.code_model.options["models_mode"]:
77
68
  file_import.add_submodule_import(local_path, "models", ImportType.LOCAL, alias="_models")
@@ -147,6 +147,8 @@ class PagingOperation(Operation):
147
147
 
148
148
  def imports(self, async_mode: bool) -> FileImport:
149
149
  file_import = super(PagingOperation, self).imports(async_mode)
150
+ # operation adds an import for distributed_trace_async, we don't want it
151
+ file_import.imports = [i for i in file_import.imports if not i.submodule_name == "distributed_trace_async"]
150
152
 
151
153
  pager_import_path = ".".join(self.get_pager_path(async_mode).split(".")[:-1])
152
154
  pager = self.get_pager(async_mode)
@@ -156,11 +158,9 @@ class PagingOperation(Operation):
156
158
  if async_mode:
157
159
  file_import.add_submodule_import("azure.core.async_paging", "AsyncList", ImportType.AZURECORE)
158
160
 
159
- if self.code_model.options["tracing"]:
161
+ if self.code_model.options["tracing"] and self.want_tracing:
160
162
  file_import.add_submodule_import(
161
163
  "azure.core.tracing.decorator", "distributed_trace", ImportType.AZURECORE,
162
164
  )
163
- if not self.code_model.options["models_mode"]:
164
- file_import.add_submodule_import("json", "loads", ImportType.STDLIB, alias="_loads")
165
165
 
166
166
  return file_import
@@ -68,7 +68,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
68
68
  grouped_by: Optional["Parameter"] = None,
69
69
  original_parameter: Optional["Parameter"] = None,
70
70
  client_default_value: Optional[Any] = None,
71
- keyword_only: bool = False,
71
+ keyword_only: Optional[bool] = None,
72
72
  content_types: Optional[List[str]] = None,
73
73
  ) -> None:
74
74
  super().__init__(yaml_data)
@@ -98,6 +98,8 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
98
98
  self.content_types = content_types or []
99
99
  self.body_kwargs: List[Parameter] = []
100
100
  self.is_body_kwarg = False
101
+ self.need_import = True
102
+ self.is_kwarg = (self.rest_api_name == "Content-Type" or (self.constant and self.rest_api_name != "Accept"))
101
103
 
102
104
  def __hash__(self) -> int:
103
105
  return hash(self.serialized_name)
@@ -110,6 +112,14 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
110
112
  if description:
111
113
  description += " "
112
114
  description += f"{self.schema.extra_description_information}"
115
+ if isinstance(self.schema, ConstantSchema) and not self.constant:
116
+ if description:
117
+ description += " "
118
+ description += f"Possible values are {self.schema.get_declaration(self.schema.value)} or {None}."
119
+ if self.has_default_value and not any(
120
+ l for l in ["default value is", "default is"] if l in description.lower()
121
+ ):
122
+ description += f" Default value is {self.default_value_declaration}."
113
123
  if self.constant:
114
124
  description += " Note that overriding this default value may result in unsupported behavior."
115
125
  return description
@@ -231,7 +241,12 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
231
241
  default_value_declaration = "None"
232
242
  else:
233
243
  if isinstance(self.schema, ConstantSchema):
234
- default_value = self.schema.get_declaration(self.schema.value)
244
+ if (self.required or
245
+ self.is_content_type or
246
+ not self.code_model.options["default_optional_constants_to_none"]):
247
+ default_value = self.schema.get_declaration(self.schema.value)
248
+ else:
249
+ default_value = None
235
250
  default_value_declaration = default_value
236
251
  else:
237
252
  default_value = self.schema.default_value
@@ -297,22 +312,26 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
297
312
  origin_name = f"self._config.{self.serialized_name}"
298
313
  return origin_name
299
314
 
300
- @property
301
- def is_kwarg(self) -> bool:
302
- # this means "am I in **kwargs?"
303
- return self.rest_api_name == "Content-Type" or (self.constant and self.rest_api_name != "Accept")
304
-
305
315
  @property
306
316
  def is_keyword_only(self) -> bool:
307
317
  # this means in async mode, I am documented like def hello(positional_1, *, me!)
308
- return self._keyword_only
318
+ return self._keyword_only or False
319
+
320
+ @is_keyword_only.setter
321
+ def is_keyword_only(self, val: bool) -> None:
322
+ self._keyword_only = val
323
+ self.is_kwarg = False
309
324
 
310
325
  @property
311
326
  def is_hidden(self) -> bool:
312
- return self.serialized_name in _HIDDEN_KWARGS or (
327
+ return self.serialized_name in _HIDDEN_KWARGS and self.is_kwarg or (
313
328
  self.yaml_data["implementation"] == "Client" and self.constant
314
329
  )
315
330
 
331
+ @property
332
+ def is_content_type(self) -> bool:
333
+ return self.rest_api_name == "Content-Type" and self.location == ParameterLocation.Header
334
+
316
335
  @property
317
336
  def is_positional(self) -> bool:
318
337
  return self.in_method_signature and not (self.is_keyword_only or self.is_kwarg)
@@ -364,6 +383,8 @@ class ParameterOnlyPathAndBodyPositional(Parameter):
364
383
 
365
384
  @property
366
385
  def is_keyword_only(self) -> bool:
386
+ if self._keyword_only is not None:
387
+ return self._keyword_only
367
388
  return self.in_method_signature and not (
368
389
  self.is_hidden or
369
390
  self.location == ParameterLocation.Path or
@@ -372,6 +393,11 @@ class ParameterOnlyPathAndBodyPositional(Parameter):
372
393
  self.is_kwarg
373
394
  )
374
395
 
396
+ @is_keyword_only.setter
397
+ def is_keyword_only(self, val: bool) -> None:
398
+ self._keyword_only = val
399
+ self.is_kwarg = False
400
+
375
401
  def get_parameter(code_model):
376
402
  if code_model.options["only_path_and_body_params_positional"]:
377
403
  return ParameterOnlyPathAndBodyPositional
@@ -207,7 +207,9 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
207
207
  lambda parameter: parameter.implementation == self.implementation
208
208
  )
209
209
  positional = [p for p in parameters_of_this_implementation if p.is_positional]
210
- keyword_only = [p for p in parameters_of_this_implementation if p.is_keyword_only]
210
+ keyword_only = self._filter_out_multiple_content_type(
211
+ [p for p in parameters_of_this_implementation if p.is_keyword_only]
212
+ )
211
213
  kwargs = self._filter_out_multiple_content_type(
212
214
  [p for p in parameters_of_this_implementation if p.is_kwarg]
213
215
  )
@@ -275,6 +277,7 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
275
277
  def _create_files_or_data_param(
276
278
  params: List[Parameter], serialized_name: str, description: str
277
279
  ) -> Parameter:
280
+ params[0].need_import = False
278
281
  param = copy(params[0])
279
282
  param.serialized_name = serialized_name
280
283
  param.schema = DictionarySchema(
@@ -314,7 +317,9 @@ class ParameterOnlyPathAndBodyPositionalList(ParameterList):
314
317
  file_and_data_params.append(data_param)
315
318
  method_params = [p for p in method_params if not p.is_multipart and not p.is_data_input]
316
319
  positional = [p for p in method_params if p.is_positional]
317
- keyword_only = [p for p in method_params if p.is_keyword_only]
320
+ keyword_only = self._filter_out_multiple_content_type(
321
+ [p for p in method_params if p.is_keyword_only]
322
+ )
318
323
  kwargs = self._filter_out_multiple_content_type(
319
324
  [p for p in method_params if p.is_kwarg]
320
325
  )
@@ -337,7 +342,9 @@ class GlobalParameterList(ParameterList):
337
342
  """
338
343
  # Client level should not be on Method, etc.
339
344
  positional = [p for p in self.parameters if p.is_positional]
340
- keyword_only = [p for p in self.parameters if p.is_keyword_only]
345
+ keyword_only = self._filter_out_multiple_content_type(
346
+ [p for p in self.parameters if p.is_keyword_only]
347
+ )
341
348
  kwargs = self._filter_out_multiple_content_type(
342
349
  [p for p in self.parameters if p.is_kwarg]
343
350
  )
@@ -383,7 +390,7 @@ class GlobalParameterList(ParameterList):
383
390
  schema=StringSchema(namespace="", yaml_data={"type": "str"}),
384
391
  rest_api_name=self.host_variable_name,
385
392
  serialized_name=self.host_variable_name,
386
- description=f"Service URL. Default value is '{host_value}'.",
393
+ description=f"Service URL.",
387
394
  implementation="Client",
388
395
  required=True,
389
396
  location=ParameterLocation.Other,
@@ -66,7 +66,8 @@ class RequestBuilder(BaseBuilder):
66
66
  def imports(self) -> FileImport:
67
67
  file_import = FileImport()
68
68
  for parameter in self.parameters:
69
- file_import.merge(parameter.imports())
69
+ if parameter.need_import:
70
+ file_import.merge(parameter.imports())
70
71
 
71
72
  file_import.add_submodule_import(
72
73
  "azure.core.rest",
@@ -87,7 +88,7 @@ class RequestBuilder(BaseBuilder):
87
88
  file_import.add_submodule_import(
88
89
  "typing", "Any", ImportType.STDLIB, typing_section=TypingSection.CONDITIONAL
89
90
  )
90
- file_import.add_submodule_import("msrest", "Serializer", ImportType.AZURECORE)
91
+ file_import.add_submodule_import("msrest", "Serializer", ImportType.THIRDPARTY)
91
92
  if self.parameters.has_body and (
92
93
  self.code_model.options["builders_visibility"] != "embedded" or
93
94
  self.code_model.options["add_python3_operation_files"]
@@ -62,6 +62,11 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
62
62
  def is_keyword_only(self) -> bool:
63
63
  return not self.location == ParameterLocation.Path and not self.is_kwarg
64
64
 
65
+ @is_keyword_only.setter
66
+ def is_keyword_only(self, val: bool) -> None:
67
+ self._keyword_only = val
68
+ self.is_kwarg = False
69
+
65
70
  @property
66
71
  def full_serialized_name(self) -> str:
67
72
  return self.serialized_name
@@ -198,6 +198,7 @@ class RequestBuilderParameterList(ParameterList):
198
198
  else:
199
199
  for kwarg in body_kwargs_added:
200
200
  kwarg.required = False
201
+ first_body_param.need_import = False
201
202
  self.parameters = body_kwargs_added + self.parameters
202
203
 
203
204
  @property