@autorest/python 5.12.6 → 5.15.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 (69) hide show
  1. package/ChangeLog.md +205 -125
  2. package/autorest/black/__init__.py +3 -0
  3. package/autorest/codegen/__init__.py +120 -48
  4. package/autorest/codegen/models/__init__.py +2 -1
  5. package/autorest/codegen/models/base_schema.py +2 -6
  6. package/autorest/codegen/models/client.py +6 -0
  7. package/autorest/codegen/models/code_model.py +43 -74
  8. package/autorest/codegen/models/constant_schema.py +7 -7
  9. package/autorest/codegen/models/credential_model.py +47 -0
  10. package/autorest/codegen/models/credential_schema.py +5 -4
  11. package/autorest/codegen/models/dictionary_schema.py +7 -7
  12. package/autorest/codegen/models/enum_schema.py +8 -39
  13. package/autorest/codegen/models/imports.py +3 -1
  14. package/autorest/codegen/models/list_schema.py +18 -8
  15. package/autorest/codegen/models/lro_operation.py +3 -3
  16. package/autorest/codegen/models/lro_paging_operation.py +3 -3
  17. package/autorest/codegen/models/object_schema.py +17 -13
  18. package/autorest/codegen/models/operation.py +38 -10
  19. package/autorest/codegen/models/operation_group.py +7 -2
  20. package/autorest/codegen/models/paging_operation.py +3 -3
  21. package/autorest/codegen/models/parameter.py +71 -22
  22. package/autorest/codegen/models/parameter_list.py +11 -5
  23. package/autorest/codegen/models/primitive_schemas.py +15 -25
  24. package/autorest/codegen/models/property.py +5 -5
  25. package/autorest/codegen/models/request_builder.py +4 -4
  26. package/autorest/codegen/models/request_builder_parameter.py +17 -5
  27. package/autorest/codegen/models/schema_response.py +23 -10
  28. package/autorest/codegen/models/utils.py +20 -0
  29. package/autorest/codegen/serializers/__init__.py +184 -87
  30. package/autorest/codegen/serializers/builder_serializer.py +113 -47
  31. package/autorest/codegen/serializers/client_serializer.py +16 -6
  32. package/autorest/codegen/serializers/general_serializer.py +28 -4
  33. package/autorest/codegen/serializers/import_serializer.py +1 -1
  34. package/autorest/codegen/serializers/metadata_serializer.py +1 -1
  35. package/autorest/codegen/serializers/model_base_serializer.py +8 -0
  36. package/autorest/codegen/serializers/model_python3_serializer.py +2 -2
  37. package/autorest/codegen/serializers/operation_groups_serializer.py +1 -0
  38. package/autorest/codegen/serializers/patch_serializer.py +12 -3
  39. package/autorest/codegen/serializers/utils.py +29 -4
  40. package/autorest/codegen/templates/CHANGELOG.md.jinja2 +6 -0
  41. package/autorest/codegen/templates/LICENSE.jinja2 +21 -0
  42. package/autorest/codegen/templates/MANIFEST.in.jinja2 +7 -0
  43. package/autorest/codegen/templates/README.md.jinja2 +105 -0
  44. package/autorest/codegen/templates/config.py.jinja2 +4 -4
  45. package/autorest/codegen/templates/dev_requirements.txt.jinja2 +10 -0
  46. package/autorest/codegen/templates/enum.py.jinja2 +1 -1
  47. package/autorest/codegen/templates/enum_container.py.jinja2 +0 -1
  48. package/autorest/codegen/templates/init.py.jinja2 +9 -6
  49. package/autorest/codegen/templates/keywords.jinja2 +14 -1
  50. package/autorest/codegen/templates/lro_operation.py.jinja2 +1 -1
  51. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +1 -1
  52. package/autorest/codegen/templates/metadata.json.jinja2 +3 -3
  53. package/autorest/codegen/templates/model.py.jinja2 +1 -6
  54. package/autorest/codegen/templates/model_init.py.jinja2 +7 -4
  55. package/autorest/codegen/templates/operation.py.jinja2 +2 -3
  56. package/autorest/codegen/templates/operation_group.py.jinja2 +20 -17
  57. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +0 -1
  58. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
  59. package/autorest/codegen/templates/paging_operation.py.jinja2 +1 -1
  60. package/autorest/codegen/templates/patch.py.jinja2 +18 -29
  61. package/autorest/codegen/templates/request_builder.py.jinja2 +4 -6
  62. package/autorest/codegen/templates/setup.py.jinja2 +79 -20
  63. package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
  64. package/autorest/multiapi/models/imports.py +21 -11
  65. package/autorest/multiapi/serializers/import_serializer.py +3 -1
  66. package/autorest/namer/name_converter.py +1 -1
  67. package/package.json +2 -2
  68. package/run-python3.js +1 -7
  69. package/venvtools.py +2 -2
@@ -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
  )
@@ -315,7 +317,9 @@ class ParameterOnlyPathAndBodyPositionalList(ParameterList):
315
317
  file_and_data_params.append(data_param)
316
318
  method_params = [p for p in method_params if not p.is_multipart and not p.is_data_input]
317
319
  positional = [p for p in method_params if p.is_positional]
318
- 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
+ )
319
323
  kwargs = self._filter_out_multiple_content_type(
320
324
  [p for p in method_params if p.is_kwarg]
321
325
  )
@@ -338,7 +342,9 @@ class GlobalParameterList(ParameterList):
338
342
  """
339
343
  # Client level should not be on Method, etc.
340
344
  positional = [p for p in self.parameters if p.is_positional]
341
- 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
+ )
342
348
  kwargs = self._filter_out_multiple_content_type(
343
349
  [p for p in self.parameters if p.is_kwarg]
344
350
  )
@@ -384,7 +390,7 @@ class GlobalParameterList(ParameterList):
384
390
  schema=StringSchema(namespace="", yaml_data={"type": "str"}),
385
391
  rest_api_name=self.host_variable_name,
386
392
  serialized_name=self.host_variable_name,
387
- description=f"Service URL. Default value is '{host_value}'.",
393
+ description=f"Service URL.",
388
394
  implementation="Client",
389
395
  required=True,
390
396
  location=ParameterLocation.Other,
@@ -399,7 +405,7 @@ class GlobalParameterList(ParameterList):
399
405
  credential_parameter = Parameter(
400
406
  self.code_model,
401
407
  yaml_data={},
402
- schema=self.code_model.credential_schema_policy.credential,
408
+ schema=self.code_model.credential_model.credential_schema_policy.credential,
403
409
  serialized_name="credential",
404
410
  rest_api_name="credential",
405
411
  implementation="Client",
@@ -39,8 +39,7 @@ class PrimitiveSchema(BaseSchema):
39
39
  def docstring_type(self) -> str:
40
40
  return self._to_python_type()
41
41
 
42
- @property
43
- def type_annotation(self) -> str:
42
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
44
43
  return self.docstring_type
45
44
 
46
45
  @property
@@ -102,8 +101,7 @@ class IOSchema(PrimitiveSchema):
102
101
  def docstring_type(self) -> str:
103
102
  return self.type
104
103
 
105
- @property
106
- def type_annotation(self) -> str:
104
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
107
105
  return self.docstring_type
108
106
 
109
107
  @property
@@ -128,8 +126,7 @@ class AnySchema(PrimitiveSchema):
128
126
  def docstring_type(self) -> str:
129
127
  return "any"
130
128
 
131
- @property
132
- def type_annotation(self) -> str:
129
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
133
130
  return "Any"
134
131
 
135
132
  @property
@@ -147,8 +144,7 @@ class JSONSchema(AnySchema):
147
144
  def docstring_type(self) -> str:
148
145
  return "JSONType"
149
146
 
150
- @property
151
- def type_annotation(self) -> str:
147
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
152
148
  return "JSONType"
153
149
 
154
150
 
@@ -206,8 +202,7 @@ class NumberSchema(PrimitiveSchema):
206
202
  return "int"
207
203
  return "float"
208
204
 
209
- @property
210
- def type_annotation(self) -> str:
205
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
211
206
  python_type = self.docstring_type
212
207
  if python_type == "long":
213
208
  return "int"
@@ -269,10 +264,9 @@ class DatetimeSchema(PrimitiveSchema):
269
264
 
270
265
  @property
271
266
  def docstring_type(self) -> str:
272
- return "~" + self.type_annotation
267
+ return "~" + self.type_annotation()
273
268
 
274
- @property
275
- def type_annotation(self) -> str:
269
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
276
270
  return "datetime.datetime"
277
271
 
278
272
  @property
@@ -301,10 +295,9 @@ class TimeSchema(PrimitiveSchema):
301
295
 
302
296
  @property
303
297
  def docstring_type(self) -> str:
304
- return "~" + self.type_annotation
298
+ return "~" + self.type_annotation()
305
299
 
306
- @property
307
- def type_annotation(self) -> str:
300
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
308
301
  return "datetime.time"
309
302
 
310
303
  @property
@@ -334,10 +327,9 @@ class UnixTimeSchema(PrimitiveSchema):
334
327
 
335
328
  @property
336
329
  def docstring_type(self) -> str:
337
- return "~" + self.type_annotation
330
+ return "~" + self.type_annotation()
338
331
 
339
- @property
340
- def type_annotation(self) -> str:
332
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
341
333
  return "datetime.datetime"
342
334
 
343
335
  @property
@@ -367,10 +359,9 @@ class DateSchema(PrimitiveSchema):
367
359
 
368
360
  @property
369
361
  def docstring_type(self) -> str:
370
- return "~" + self.type_annotation
362
+ return "~" + self.type_annotation()
371
363
 
372
- @property
373
- def type_annotation(self) -> str:
364
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
374
365
  return "datetime.date"
375
366
 
376
367
  @property
@@ -400,10 +391,9 @@ class DurationSchema(PrimitiveSchema):
400
391
 
401
392
  @property
402
393
  def docstring_type(self) -> str:
403
- return "~" + self.type_annotation
394
+ return "~" + self.type_annotation()
404
395
 
405
- @property
406
- def type_annotation(self) -> str:
396
+ def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
407
397
  return "datetime.timedelta"
408
398
 
409
399
  @property
@@ -60,7 +60,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
60
60
  values = [self.schema.enum_type.get_declaration(v.value) for v in self.schema.values]
61
61
  if description and description[-1] != " ":
62
62
  description += " "
63
- description += "Possible values include: {}.".format(", ".join(values))
63
+ description += "Known values are: {}.".format(", ".join(values))
64
64
  if self.schema.default_value:
65
65
  description += f' Default value: "{self.schema.default_value}".'
66
66
  return description
@@ -149,11 +149,10 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
149
149
  return self.schema.get_declaration(self.client_default_value)
150
150
  return self.schema.default_value_declaration
151
151
 
152
- @property
153
- def type_annotation(self) -> str:
152
+ def type_annotation(self, *, is_operation_file: bool = False) -> str:
154
153
  if self.required:
155
- return self.schema.type_annotation
156
- return f"Optional[{self.schema.type_annotation}]"
154
+ return self.schema.type_annotation(is_operation_file=is_operation_file)
155
+ return f"Optional[{self.schema.type_annotation(is_operation_file=is_operation_file)}]"
157
156
 
158
157
  def get_json_template_representation(self, **kwargs: Any) -> Any:
159
158
  kwargs["optional"] = not self.required
@@ -171,4 +170,5 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
171
170
  file_import = self.schema.model_file_imports()
172
171
  if not self.required:
173
172
  file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
173
+ file_import.merge(self.schema.model_file_imports())
174
174
  return file_import
@@ -82,9 +82,7 @@ class RequestBuilder(BaseBuilder):
82
82
  f"{relative_path}_vendor", "_format_url_section", ImportType.LOCAL
83
83
  )
84
84
  if self.parameters.headers or self.parameters.query:
85
- file_import.add_submodule_import(
86
- "typing", "Dict", ImportType.STDLIB, typing_section=TypingSection.CONDITIONAL
87
- )
85
+ file_import.add_submodule_import("azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE)
88
86
  file_import.add_submodule_import(
89
87
  "typing", "Any", ImportType.STDLIB, typing_section=TypingSection.CONDITIONAL
90
88
  )
@@ -130,7 +128,9 @@ class RequestBuilder(BaseBuilder):
130
128
  schema_requests=schema_requests,
131
129
  parameters=parameter_list,
132
130
  description=yaml_data["language"]["python"]["description"],
133
- responses=[SchemaResponse.from_yaml(yaml) for yaml in yaml_data.get("responses", [])],
131
+ responses=[
132
+ SchemaResponse.from_yaml(yaml, code_model=code_model) for yaml in yaml_data.get("responses", [])
133
+ ],
134
134
  summary=yaml_data["language"]["python"].get("summary"),
135
135
  )
136
136
  code_model.request_builder_ids[id(yaml_data)] = request_builder_class
@@ -4,7 +4,8 @@
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
6
  from typing import Any, Dict, List, Optional
7
- from .parameter import ParameterOnlyPathAndBodyPositional, ParameterLocation, ParameterStyle
7
+ from .parameter import ParameterOnlyPathAndBodyPositional, ParameterLocation, ParameterStyle, get_target_property_name
8
+ from .utils import get_schema
8
9
 
9
10
  def _make_public(name):
10
11
  if name[0] == "_":
@@ -16,8 +17,8 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
16
17
  @property
17
18
  def in_method_signature(self) -> bool:
18
19
  return not(
19
- # don't put accept in method signature
20
- self.rest_api_name == "Accept"
20
+ # if not inputtable, don't put in signature
21
+ not self.inputtable_by_user
21
22
  # If i'm not in the method code, no point in being in signature
22
23
  or not self.in_method_code
23
24
  # If I'm a flattened property of a body, don't want me, want the body param
@@ -62,6 +63,11 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
62
63
  def is_keyword_only(self) -> bool:
63
64
  return not self.location == ParameterLocation.Path and not self.is_kwarg
64
65
 
66
+ @is_keyword_only.setter
67
+ def is_keyword_only(self, val: bool) -> None:
68
+ self._keyword_only = val
69
+ self.is_kwarg = False
70
+
65
71
  @property
66
72
  def full_serialized_name(self) -> str:
67
73
  return self.serialized_name
@@ -73,10 +79,16 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
73
79
  http_protocol = yaml_data["protocol"].get("http", {"in": ParameterLocation.Other})
74
80
  name = yaml_data["language"]["python"]["name"]
75
81
  location = ParameterLocation(http_protocol["in"])
82
+ serialized_name = yaml_data["language"]["python"]["name"]
83
+ schema = get_schema(
84
+ code_model, yaml_data.get("schema"), serialized_name
85
+ )
86
+ target_property = yaml_data.get("targetProperty")
87
+ target_property_name = get_target_property_name(code_model, id(target_property)) if target_property else None
76
88
  return cls(
77
89
  code_model=code_model,
78
90
  yaml_data=yaml_data,
79
- schema=yaml_data.get("schema", None), # FIXME replace by operation model
91
+ schema=schema,
80
92
  # See also https://github.com/Azure/autorest.modelerfour/issues/80
81
93
  rest_api_name=yaml_data["language"]["default"].get(
82
94
  "serializedName", yaml_data["language"]["default"]["name"]
@@ -88,7 +100,7 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
88
100
  location=location,
89
101
  skip_url_encoding=yaml_data.get("extensions", {}).get("x-ms-skip-url-encoding", False),
90
102
  constraints=[], # FIXME constraints
91
- target_property_name=id(yaml_data["targetProperty"]) if yaml_data.get("targetProperty") else None,
103
+ target_property_name=target_property_name,
92
104
  style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None,
93
105
  explode=http_protocol.get("explode", False),
94
106
  grouped_by=yaml_data.get("groupedBy", None),
@@ -3,12 +3,17 @@
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 Dict, Optional, List, Union, Any, cast
6
+ from typing import Dict, Optional, List, Union, Any, cast, TYPE_CHECKING
7
7
 
8
8
  from .base_model import BaseModel
9
9
  from .base_schema import BaseSchema
10
10
  from .object_schema import ObjectSchema
11
11
  from .imports import FileImport, ImportType
12
+ from .utils import get_schema
13
+ from .primitive_schemas import IOSchema
14
+
15
+ if TYPE_CHECKING:
16
+ from .code_model import CodeModel
12
17
 
13
18
 
14
19
  class HeaderResponse:
@@ -24,6 +29,7 @@ class HeaderResponse:
24
29
  class SchemaResponse(BaseModel):
25
30
  def __init__(
26
31
  self,
32
+ code_model: "CodeModel",
27
33
  yaml_data: Dict[str, Any],
28
34
  schema: Optional[BaseSchema],
29
35
  content_types: List[str],
@@ -32,6 +38,7 @@ class SchemaResponse(BaseModel):
32
38
  binary: bool,
33
39
  ) -> None:
34
40
  super().__init__(yaml_data)
41
+ self.code_model = code_model
35
42
  self.schema = schema
36
43
  self.content_types = content_types
37
44
  self.status_codes = status_codes
@@ -57,13 +64,12 @@ class SchemaResponse(BaseModel):
57
64
  return self.schema.serialization_type
58
65
  return "None"
59
66
 
60
- @property
61
- def operation_type_annotation(self) -> str:
67
+ def type_annotation(self, *, is_operation_file: bool = False) -> str:
62
68
  if not self.schema:
63
69
  return "None"
64
70
  if self.nullable:
65
- return f"Optional[{self.schema.operation_type_annotation}]"
66
- return self.schema.operation_type_annotation
71
+ return f"Optional[{self.schema.type_annotation(is_operation_file=is_operation_file)}]"
72
+ return self.schema.type_annotation(is_operation_file=is_operation_file)
67
73
 
68
74
  @property
69
75
  def docstring_text(self) -> str:
@@ -103,20 +109,27 @@ class SchemaResponse(BaseModel):
103
109
  return file_import
104
110
 
105
111
  @classmethod
106
- def from_yaml(cls, yaml_data: Dict[str, Any]) -> "SchemaResponse":
107
-
112
+ def from_yaml(cls, yaml_data: Dict[str, Any], *, code_model: "CodeModel") -> "SchemaResponse":
113
+ binary = yaml_data.get("binary", False)
114
+ if binary:
115
+ schema: BaseSchema = IOSchema(namespace=None, yaml_data={})
116
+ else:
117
+ schema = get_schema(code_model, yaml_data.get("schema"))
108
118
  return cls(
119
+ code_model=code_model,
109
120
  yaml_data=yaml_data,
110
- schema=yaml_data.get("schema", None), # FIXME replace by operation model
121
+ schema=schema,
111
122
  content_types=yaml_data["protocol"]["http"].get("mediaTypes", []),
112
123
  status_codes=[
113
124
  int(code) if code != "default" else "default" for code in yaml_data["protocol"]["http"]["statusCodes"]
114
125
  ],
115
126
  headers=[
116
- HeaderResponse(header_prop["header"], header_prop["schema"])
127
+ HeaderResponse(
128
+ header_prop["header"], get_schema(code_model, header_prop["schema"], header_prop["header"])
129
+ )
117
130
  for header_prop in yaml_data["protocol"]["http"].get("headers", [])
118
131
  ],
119
- binary=yaml_data.get("binary", False),
132
+ binary=binary,
120
133
  )
121
134
 
122
135
  def __repr__(self) -> str:
@@ -4,5 +4,25 @@
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
6
  import re
7
+ from typing import Any, TYPE_CHECKING
8
+ import logging
9
+ from .base_schema import BaseSchema
10
+
11
+ if TYPE_CHECKING:
12
+ from .code_model import CodeModel
13
+
14
+
15
+ _LOGGER = logging.getLogger(__name__)
7
16
 
8
17
  JSON_REGEXP = re.compile(r'^(application|text)/(.+\+)?json$')
18
+
19
+ def get_schema(code_model: "CodeModel", schema: Any, serialized_name: str = "unknown") -> BaseSchema:
20
+ if not isinstance(schema, dict):
21
+ return schema
22
+ schema_id = id(schema)
23
+ _LOGGER.debug("Looking for id %s for member %s", schema_id, serialized_name)
24
+ try:
25
+ return code_model.lookup_schema(schema_id)
26
+ except KeyError:
27
+ _LOGGER.critical("Unable to ref the object")
28
+ raise