@autorest/python 5.15.0 → 5.16.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 (86) hide show
  1. package/ChangeLog.md +20 -0
  2. package/autorest/__init__.py +1 -2
  3. package/autorest/black/__init__.py +12 -5
  4. package/autorest/codegen/__init__.py +145 -73
  5. package/autorest/codegen/models/__init__.py +29 -18
  6. package/autorest/codegen/models/base_builder.py +48 -11
  7. package/autorest/codegen/models/base_model.py +6 -4
  8. package/autorest/codegen/models/base_schema.py +19 -18
  9. package/autorest/codegen/models/client.py +65 -21
  10. package/autorest/codegen/models/code_model.py +107 -61
  11. package/autorest/codegen/models/constant_schema.py +25 -13
  12. package/autorest/codegen/models/credential_model.py +23 -15
  13. package/autorest/codegen/models/credential_schema.py +18 -14
  14. package/autorest/codegen/models/credential_schema_policy.py +11 -15
  15. package/autorest/codegen/models/dictionary_schema.py +20 -17
  16. package/autorest/codegen/models/enum_schema.py +35 -25
  17. package/autorest/codegen/models/imports.py +70 -41
  18. package/autorest/codegen/models/list_schema.py +25 -13
  19. package/autorest/codegen/models/lro_operation.py +58 -22
  20. package/autorest/codegen/models/lro_paging_operation.py +2 -3
  21. package/autorest/codegen/models/object_schema.py +99 -49
  22. package/autorest/codegen/models/operation.py +236 -117
  23. package/autorest/codegen/models/operation_group.py +64 -34
  24. package/autorest/codegen/models/paging_operation.py +45 -18
  25. package/autorest/codegen/models/parameter.py +151 -83
  26. package/autorest/codegen/models/parameter_list.py +183 -162
  27. package/autorest/codegen/models/primitive_schemas.py +84 -55
  28. package/autorest/codegen/models/property.py +44 -26
  29. package/autorest/codegen/models/request_builder.py +65 -30
  30. package/autorest/codegen/models/request_builder_parameter.py +47 -23
  31. package/autorest/codegen/models/request_builder_parameter_list.py +77 -108
  32. package/autorest/codegen/models/schema_request.py +16 -6
  33. package/autorest/codegen/models/schema_response.py +18 -13
  34. package/autorest/codegen/models/utils.py +5 -2
  35. package/autorest/codegen/serializers/__init__.py +182 -91
  36. package/autorest/codegen/serializers/builder_serializer.py +667 -331
  37. package/autorest/codegen/serializers/client_serializer.py +98 -37
  38. package/autorest/codegen/serializers/general_serializer.py +61 -26
  39. package/autorest/codegen/serializers/import_serializer.py +93 -31
  40. package/autorest/codegen/serializers/metadata_serializer.py +73 -24
  41. package/autorest/codegen/serializers/model_base_serializer.py +35 -15
  42. package/autorest/codegen/serializers/model_generic_serializer.py +1 -4
  43. package/autorest/codegen/serializers/model_init_serializer.py +5 -1
  44. package/autorest/codegen/serializers/model_python3_serializer.py +7 -6
  45. package/autorest/codegen/serializers/operation_groups_serializer.py +20 -9
  46. package/autorest/codegen/serializers/operations_init_serializer.py +23 -11
  47. package/autorest/codegen/serializers/patch_serializer.py +4 -1
  48. package/autorest/codegen/serializers/{rest_serializer.py → request_builders_serializer.py} +29 -12
  49. package/autorest/codegen/serializers/utils.py +35 -21
  50. package/autorest/codegen/templates/init.py.jinja2 +2 -2
  51. package/autorest/codegen/templates/lro_operation.py.jinja2 +5 -7
  52. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +5 -7
  53. package/autorest/codegen/templates/metadata.json.jinja2 +7 -6
  54. package/autorest/codegen/templates/operation.py.jinja2 +7 -9
  55. package/autorest/codegen/templates/operation_group.py.jinja2 +2 -8
  56. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
  57. package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
  58. package/autorest/codegen/templates/request_builder.py.jinja2 +18 -11
  59. package/autorest/jsonrpc/__init__.py +7 -12
  60. package/autorest/jsonrpc/localapi.py +4 -3
  61. package/autorest/jsonrpc/server.py +13 -6
  62. package/autorest/jsonrpc/stdstream.py +13 -6
  63. package/autorest/m2r/__init__.py +5 -8
  64. package/autorest/multiapi/__init__.py +24 -14
  65. package/autorest/multiapi/models/client.py +21 -11
  66. package/autorest/multiapi/models/code_model.py +23 -10
  67. package/autorest/multiapi/models/config.py +4 -1
  68. package/autorest/multiapi/models/constant_global_parameter.py +1 -0
  69. package/autorest/multiapi/models/global_parameter.py +2 -1
  70. package/autorest/multiapi/models/global_parameters.py +14 -8
  71. package/autorest/multiapi/models/imports.py +24 -17
  72. package/autorest/multiapi/models/mixin_operation.py +5 -5
  73. package/autorest/multiapi/models/operation_group.py +2 -1
  74. package/autorest/multiapi/models/operation_mixin_group.py +21 -10
  75. package/autorest/multiapi/serializers/__init__.py +18 -23
  76. package/autorest/multiapi/serializers/import_serializer.py +47 -17
  77. package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
  78. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +1 -1
  79. package/autorest/multiapi/utils.py +3 -3
  80. package/autorest/namer/__init__.py +2 -4
  81. package/autorest/namer/name_converter.py +200 -103
  82. package/autorest/namer/python_mappings.py +10 -22
  83. package/package.json +2 -2
  84. package/run-python3.js +2 -3
  85. package/venvtools.py +1 -1
  86. package/autorest/codegen/models/rest.py +0 -42
@@ -26,6 +26,13 @@ _LOGGER = logging.getLogger(__name__)
26
26
  _HIDDEN_KWARGS = ["content_type"]
27
27
 
28
28
 
29
+ class ParameterMethodLocation(str, Enum):
30
+ POSITIONAL = "positional"
31
+ KEYWORD_ONLY = "keyword_only"
32
+ KWARG = "kwarg"
33
+ HIDDEN_KWARG = "hidden_kwarg"
34
+
35
+
29
36
  class ParameterLocation(Enum):
30
37
  Path = "path"
31
38
  Body = "body"
@@ -50,7 +57,6 @@ class ParameterStyle(Enum):
50
57
  multipart = "multipart"
51
58
 
52
59
 
53
-
54
60
  def get_target_property_name(code_model: "CodeModel", target_property_id: int) -> str:
55
61
  for obj in code_model.schemas.values():
56
62
  for prop in obj.properties:
@@ -59,11 +65,13 @@ def get_target_property_name(code_model: "CodeModel", target_property_id: int) -
59
65
  raise KeyError("Didn't find the target property")
60
66
 
61
67
 
62
- class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too-many-public-methods
68
+ class Parameter(
69
+ BaseModel
70
+ ): # pylint: disable=too-many-instance-attributes, too-many-public-methods
63
71
  def __init__(
64
72
  self,
65
- code_model,
66
73
  yaml_data: Dict[str, Any],
74
+ code_model: "CodeModel",
67
75
  schema: BaseSchema,
68
76
  rest_api_name: str,
69
77
  serialized_name: str,
@@ -73,7 +81,9 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
73
81
  location: ParameterLocation,
74
82
  skip_url_encoding: bool,
75
83
  constraints: List[Any],
76
- target_property_name: Optional[Union[int, str]] = None, # first uses id as placeholder
84
+ target_property_name: Optional[
85
+ Union[int, str]
86
+ ] = None, # first uses id as placeholder
77
87
  style: Optional[ParameterStyle] = None,
78
88
  explode: Optional[bool] = False,
79
89
  *,
@@ -81,10 +91,9 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
81
91
  grouped_by: Optional["Parameter"] = None,
82
92
  original_parameter: Optional["Parameter"] = None,
83
93
  client_default_value: Optional[Any] = None,
84
- keyword_only: Optional[bool] = None,
85
94
  content_types: Optional[List[str]] = None,
86
95
  ) -> None:
87
- super().__init__(yaml_data)
96
+ super().__init__(yaml_data, code_model)
88
97
  self.code_model = code_model
89
98
  self.schema = schema
90
99
  self.rest_api_name = rest_api_name
@@ -105,14 +114,17 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
105
114
  self.has_multiple_content_types: bool = False
106
115
  self.multiple_content_types_type_annot: Optional[str] = None
107
116
  self.multiple_content_types_docstring_type: Optional[str] = None
108
- self._keyword_only = keyword_only
109
- self.is_multipart = yaml_data.get("language", {}).get("python", {}).get("multipart", False)
110
- self.is_data_input = yaml_data.get("isPartialBody", False) and not self.is_multipart
117
+ self.is_multipart = (
118
+ yaml_data.get("language", {}).get("python", {}).get("multipart", False)
119
+ )
120
+ self.is_data_input = (
121
+ yaml_data.get("isPartialBody", False) and not self.is_multipart
122
+ )
111
123
  self.content_types = content_types or []
112
124
  self.body_kwargs: List[Parameter] = []
113
125
  self.is_body_kwarg = False
114
126
  self.need_import = True
115
- self.is_kwarg = (self.rest_api_name == "Content-Type" or (self.constant and self.inputtable_by_user))
127
+ self._method_location: Optional[ParameterMethodLocation] = None
116
128
 
117
129
  def __hash__(self) -> int:
118
130
  return hash(self.serialized_name)
@@ -130,7 +142,9 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
130
142
  description += " "
131
143
  description += f"Known values are {self.schema.get_declaration(self.schema.value)} or {None}."
132
144
  if self.has_default_value and not any(
133
- l for l in ["default value is", "default is"] if l in description.lower()
145
+ l
146
+ for l in ["default value is", "default is"]
147
+ if l in description.lower()
134
148
  ):
135
149
  description += f" Default value is {self.default_value_declaration}."
136
150
  if self.constant:
@@ -173,12 +187,12 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
173
187
  return self.schema.get_declaration(self.schema.value)
174
188
  raise ValueError(
175
189
  "Trying to get constant declaration for a schema that is not ConstantSchema"
176
- )
190
+ )
177
191
  raise ValueError("Trying to get a declaration for a schema that doesn't exist")
178
192
 
179
193
  @property
180
194
  def serialization_formats(self) -> List[str]:
181
- return self.yaml_data.get('serializationFormats', [])
195
+ return self.yaml_data.get("serializationFormats", [])
182
196
 
183
197
  @property
184
198
  def xml_serialization_ctxt(self) -> str:
@@ -201,7 +215,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
201
215
 
202
216
  @property
203
217
  def in_method_signature(self) -> bool:
204
- return not(
218
+ return not (
205
219
  # if not inputtable, don't put in signature
206
220
  not self.inputtable_by_user
207
221
  # If i'm not in the method code, no point in being in signature
@@ -218,15 +232,20 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
218
232
  raise ValueError("Should only be calling if your parameter is grouped")
219
233
  try:
220
234
  return next(
221
- p for p in cast(ObjectSchema, self.grouped_by.schema).properties
222
- if any(op for op in p.yaml_data['originalParameter'] if id(op) == self.id)
235
+ p
236
+ for p in cast(ObjectSchema, self.grouped_by.schema).properties
237
+ if any(
238
+ op for op in p.yaml_data["originalParameter"] if id(op) == self.id
239
+ )
223
240
  )
224
241
  except StopIteration:
225
- raise ValueError("There is not a corresponding grouped property for your parameter.")
242
+ raise ValueError(
243
+ "There is not a corresponding grouped property for your parameter."
244
+ )
226
245
 
227
246
  @property
228
247
  def in_method_code(self) -> bool:
229
- return self.rest_api_name != '$host'
248
+ return self.rest_api_name != "$host"
230
249
 
231
250
  @property
232
251
  def implementation(self) -> str:
@@ -242,15 +261,21 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
242
261
  ) and isinstance(self.schema, IOSchema)
243
262
 
244
263
  def _default_value(self) -> Tuple[Optional[Any], str, str]:
245
- type_annot = self.multiple_content_types_type_annot or self.schema.type_annotation(is_operation_file=True)
264
+ type_annot = (
265
+ self.multiple_content_types_type_annot
266
+ or self.schema.type_annotation(is_operation_file=True)
267
+ )
246
268
  if self._is_io_json:
247
- type_annot = f"Union[{type_annot}, JSONType]"
248
- any_types = ["Any", "JSONType"]
249
- if not self.required and type_annot not in any_types and not self._is_io_json:
269
+ type_annot = f"Union[{type_annot}, Any]"
270
+ if not self.required and type_annot != "Any" and not self._is_io_json:
250
271
  type_annot = f"Optional[{type_annot}]"
251
272
 
252
273
  if self.client_default_value is not None:
253
- return self.client_default_value, self.schema.get_declaration(self.client_default_value), type_annot
274
+ return (
275
+ self.client_default_value,
276
+ self.schema.get_declaration(self.client_default_value),
277
+ type_annot,
278
+ )
254
279
 
255
280
  if self.multiple_content_types_type_annot:
256
281
  # means this parameter has multiple media types. We force default value to be None.
@@ -258,9 +283,11 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
258
283
  default_value_declaration = "None"
259
284
  else:
260
285
  if isinstance(self.schema, ConstantSchema):
261
- if (self.required or
262
- self.is_content_type or
263
- not self.code_model.options["default_optional_constants_to_none"]):
286
+ if (
287
+ self.required
288
+ or self.is_content_type
289
+ or not self.code_model.options["default_optional_constants_to_none"]
290
+ ):
264
291
  default_value = self.schema.get_declaration(self.schema.value)
265
292
  else:
266
293
  default_value = None
@@ -271,18 +298,36 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
271
298
  if default_value is not None and self.required:
272
299
  _LOGGER.warning(
273
300
  "Parameter '%s' is required and has a default value, this combination is not recommended",
274
- self.rest_api_name
301
+ self.rest_api_name,
275
302
  )
276
303
 
277
304
  return default_value, default_value_declaration, type_annot
278
305
 
279
306
  @property
280
307
  def description_keyword(self) -> str:
281
- return "keyword" if self.is_kwarg or self.is_keyword_only else "param"
308
+ return (
309
+ "keyword"
310
+ if self.method_location
311
+ in (
312
+ ParameterMethodLocation.KWARG,
313
+ ParameterMethodLocation.HIDDEN_KWARG,
314
+ ParameterMethodLocation.KEYWORD_ONLY,
315
+ )
316
+ else "param"
317
+ )
282
318
 
283
319
  @property
284
320
  def docstring_type_keyword(self) -> str:
285
- return "paramtype" if self.is_kwarg or self.is_keyword_only else "type"
321
+ return (
322
+ "paramtype"
323
+ if self.method_location
324
+ in (
325
+ ParameterMethodLocation.KWARG,
326
+ ParameterMethodLocation.HIDDEN_KWARG,
327
+ ParameterMethodLocation.KEYWORD_ONLY,
328
+ )
329
+ else "type"
330
+ )
286
331
 
287
332
  @property
288
333
  def default_value(self) -> Optional[Any]:
@@ -294,7 +339,9 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
294
339
  def default_value_declaration(self) -> Optional[Any]:
295
340
  return self._default_value()[1]
296
341
 
297
- def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
342
+ def type_annotation(
343
+ self, *, is_operation_file: bool = False # pylint: disable=unused-argument
344
+ ) -> str:
298
345
  return self._default_value()[2]
299
346
 
300
347
  @property
@@ -303,9 +350,11 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
303
350
 
304
351
  @property
305
352
  def docstring_type(self) -> str:
306
- retval = self.multiple_content_types_docstring_type or self.schema.docstring_type
353
+ retval = (
354
+ self.multiple_content_types_docstring_type or self.schema.docstring_type
355
+ )
307
356
  if self._is_io_json:
308
- retval += " or JSONType"
357
+ retval += " or Any"
309
358
  return retval
310
359
 
311
360
  @property
@@ -329,49 +378,52 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
329
378
  origin_name = f"self._config.{self.serialized_name}"
330
379
  return origin_name
331
380
 
332
- @property
333
- def is_keyword_only(self) -> bool:
334
- # this means in async mode, I am documented like def hello(positional_1, *, me!)
335
- return self._keyword_only or False
336
-
337
- @is_keyword_only.setter
338
- def is_keyword_only(self, val: bool) -> None:
339
- self._keyword_only = val
340
- self.is_kwarg = False
341
-
342
- @property
343
- def is_hidden(self) -> bool:
344
- return self.serialized_name in _HIDDEN_KWARGS and self.is_kwarg or (
345
- self.yaml_data["implementation"] == "Client" and self.constant
346
- )
347
-
348
381
  @property
349
382
  def is_content_type(self) -> bool:
350
- return self.rest_api_name == "Content-Type" and self.location == ParameterLocation.Header
383
+ return (
384
+ self.rest_api_name == "Content-Type"
385
+ and self.location == ParameterLocation.Header
386
+ )
351
387
 
352
388
  @property
353
- def is_positional(self) -> bool:
354
- return self.in_method_signature and not (self.is_keyword_only or self.is_kwarg)
389
+ def method_location(self) -> ParameterMethodLocation:
390
+ if self._method_location:
391
+ return self._method_location
392
+ if self.serialized_name in _HIDDEN_KWARGS or (
393
+ self._implementation == "Client" and self.constant
394
+ ):
395
+ return ParameterMethodLocation.HIDDEN_KWARG
396
+ if self.constant and self.inputtable_by_user:
397
+ return ParameterMethodLocation.KWARG
398
+ return ParameterMethodLocation.POSITIONAL
399
+
400
+ @method_location.setter
401
+ def method_location(self, val: ParameterMethodLocation) -> None:
402
+ self._method_location = val
355
403
 
356
404
  @classmethod
357
405
  def from_yaml(
358
406
  cls,
359
407
  yaml_data: Dict[str, Any],
408
+ code_model: "CodeModel",
360
409
  *,
361
- code_model,
362
- content_types: Optional[List[str]] = None
410
+ content_types: Optional[List[str]] = None,
363
411
  ) -> "Parameter":
364
- http_protocol = yaml_data["protocol"].get("http", {"in": ParameterLocation.Other})
365
- serialized_name = yaml_data["language"]["python"]["name"]
366
- schema = get_schema(
367
- code_model, yaml_data.get("schema"), serialized_name
412
+ http_protocol = yaml_data["protocol"].get(
413
+ "http", {"in": ParameterLocation.Other}
368
414
  )
415
+ serialized_name = yaml_data["language"]["python"]["name"]
416
+ schema = get_schema(code_model, yaml_data.get("schema"), serialized_name)
369
417
  target_property = yaml_data.get("targetProperty")
370
- target_property_name = get_target_property_name(code_model, id(target_property)) if target_property else None
418
+ target_property_name = (
419
+ get_target_property_name(code_model, id(target_property))
420
+ if target_property
421
+ else None
422
+ )
371
423
 
372
424
  return cls(
373
- code_model=code_model,
374
425
  yaml_data=yaml_data,
426
+ code_model=code_model,
375
427
  schema=schema, # FIXME replace by operation model
376
428
  # See also https://github.com/Azure/autorest.modelerfour/issues/80
377
429
  rest_api_name=yaml_data["language"]["default"].get(
@@ -382,45 +434,61 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
382
434
  implementation=yaml_data["implementation"],
383
435
  required=yaml_data.get("required", False),
384
436
  location=ParameterLocation(http_protocol["in"]),
385
- skip_url_encoding=yaml_data.get("extensions", {}).get("x-ms-skip-url-encoding", False),
437
+ skip_url_encoding=yaml_data.get("extensions", {}).get(
438
+ "x-ms-skip-url-encoding", False
439
+ ),
386
440
  constraints=[], # FIXME constraints
387
441
  target_property_name=target_property_name,
388
- style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None,
442
+ style=ParameterStyle(http_protocol["style"])
443
+ if "style" in http_protocol
444
+ else None,
389
445
  explode=http_protocol.get("explode", False),
390
446
  grouped_by=yaml_data.get("groupedBy", None),
391
447
  original_parameter=yaml_data.get("originalParameter", None),
392
448
  flattened=yaml_data.get("flattened", False),
393
449
  client_default_value=yaml_data.get("clientDefaultValue"),
394
- content_types=content_types
450
+ content_types=content_types,
395
451
  )
396
452
 
397
453
  def imports(self) -> FileImport:
398
- file_import = self.schema.imports()
399
- if not self.required:
400
- file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
401
- if self.has_multiple_content_types or self._is_io_json:
402
- file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
454
+ if self.need_import:
455
+ file_import = self.schema.imports()
456
+ if not self.required:
457
+ file_import.add_submodule_import(
458
+ "typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL
459
+ )
460
+ if self.has_multiple_content_types or self._is_io_json:
461
+ file_import.add_submodule_import(
462
+ "typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
463
+ )
403
464
 
404
- return file_import
465
+ return file_import
466
+ return FileImport()
405
467
 
406
- class ParameterOnlyPathAndBodyPositional(Parameter):
407
468
 
469
+ class ParameterOnlyPathAndBodyPositional(Parameter):
408
470
  @property
409
- def is_keyword_only(self) -> bool:
410
- if self._keyword_only is not None:
411
- return self._keyword_only
412
- return self.in_method_signature and not (
413
- self.is_hidden or
414
- self.location == ParameterLocation.Path or
415
- self.location == ParameterLocation.Uri or
416
- self.location == ParameterLocation.Body or
417
- self.is_kwarg
418
- )
471
+ def method_location(self) -> ParameterMethodLocation:
472
+ super_method_location = super().method_location
473
+ if super_method_location in (
474
+ ParameterMethodLocation.KWARG,
475
+ ParameterMethodLocation.HIDDEN_KWARG,
476
+ ):
477
+ return super_method_location
478
+ if self._method_location:
479
+ return self._method_location
480
+ if self.location not in (
481
+ ParameterLocation.Path,
482
+ ParameterLocation.Uri,
483
+ ParameterLocation.Body,
484
+ ):
485
+ return ParameterMethodLocation.KEYWORD_ONLY
486
+ return super_method_location
487
+
488
+ @method_location.setter
489
+ def method_location(self, val: ParameterMethodLocation) -> None:
490
+ self._method_location = val
419
491
 
420
- @is_keyword_only.setter
421
- def is_keyword_only(self, val: bool) -> None:
422
- self._keyword_only = val
423
- self.is_kwarg = False
424
492
 
425
493
  def get_parameter(code_model):
426
494
  if code_model.options["only_path_and_body_params_positional"]: