@autorest/python 5.16.0 → 5.19.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 (96) hide show
  1. package/ChangeLog.md +79 -4
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +1 -1
  4. package/autorest/codegen/__init__.py +55 -211
  5. package/autorest/codegen/models/__init__.py +116 -83
  6. package/autorest/codegen/models/base_builder.py +49 -88
  7. package/autorest/codegen/models/base_model.py +1 -1
  8. package/autorest/codegen/models/{base_schema.py → base_type.py} +61 -39
  9. package/autorest/codegen/models/client.py +165 -53
  10. package/autorest/codegen/models/code_model.py +122 -257
  11. package/autorest/codegen/models/combined_type.py +107 -0
  12. package/autorest/codegen/models/{constant_schema.py → constant_type.py} +49 -40
  13. package/autorest/codegen/models/credential_types.py +224 -0
  14. package/autorest/codegen/models/dictionary_type.py +131 -0
  15. package/autorest/codegen/models/enum_type.py +195 -0
  16. package/autorest/codegen/models/imports.py +80 -2
  17. package/autorest/codegen/models/list_type.py +149 -0
  18. package/autorest/codegen/models/lro_operation.py +79 -156
  19. package/autorest/codegen/models/lro_paging_operation.py +28 -11
  20. package/autorest/codegen/models/model_type.py +262 -0
  21. package/autorest/codegen/models/operation.py +331 -298
  22. package/autorest/codegen/models/operation_group.py +54 -91
  23. package/autorest/codegen/models/paging_operation.py +82 -123
  24. package/autorest/codegen/models/parameter.py +289 -396
  25. package/autorest/codegen/models/parameter_list.py +355 -360
  26. package/autorest/codegen/models/primitive_types.py +544 -0
  27. package/autorest/codegen/models/property.py +123 -139
  28. package/autorest/codegen/models/request_builder.py +130 -102
  29. package/autorest/codegen/models/request_builder_parameter.py +112 -100
  30. package/autorest/codegen/models/response.py +325 -0
  31. package/autorest/codegen/models/utils.py +12 -19
  32. package/autorest/codegen/serializers/__init__.py +55 -37
  33. package/autorest/codegen/serializers/builder_serializer.py +695 -1144
  34. package/autorest/codegen/serializers/client_serializer.py +92 -89
  35. package/autorest/codegen/serializers/general_serializer.py +15 -69
  36. package/autorest/codegen/serializers/import_serializer.py +7 -4
  37. package/autorest/codegen/serializers/metadata_serializer.py +15 -104
  38. package/autorest/codegen/serializers/model_base_serializer.py +49 -36
  39. package/autorest/codegen/serializers/model_generic_serializer.py +8 -6
  40. package/autorest/codegen/serializers/model_init_serializer.py +2 -4
  41. package/autorest/codegen/serializers/model_python3_serializer.py +22 -16
  42. package/autorest/codegen/serializers/operation_groups_serializer.py +7 -13
  43. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  44. package/autorest/codegen/serializers/request_builders_serializer.py +13 -30
  45. package/autorest/codegen/serializers/utils.py +0 -140
  46. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  47. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +10 -7
  48. package/autorest/codegen/templates/config.py.jinja2 +13 -13
  49. package/autorest/codegen/templates/enum.py.jinja2 +4 -4
  50. package/autorest/codegen/templates/enum_container.py.jinja2 +1 -1
  51. package/autorest/codegen/templates/init.py.jinja2 +2 -2
  52. package/autorest/codegen/templates/lro_operation.py.jinja2 +4 -1
  53. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +4 -1
  54. package/autorest/codegen/templates/metadata.json.jinja2 +33 -33
  55. package/autorest/codegen/templates/model.py.jinja2 +23 -24
  56. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  57. package/autorest/codegen/templates/model_init.py.jinja2 +3 -5
  58. package/autorest/codegen/templates/operation.py.jinja2 +6 -8
  59. package/autorest/codegen/templates/operation_group.py.jinja2 +21 -8
  60. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +2 -2
  61. package/autorest/codegen/templates/operation_tools.jinja2 +11 -3
  62. package/autorest/codegen/templates/paging_operation.py.jinja2 +2 -2
  63. package/autorest/codegen/templates/request_builder.py.jinja2 +10 -15
  64. package/autorest/codegen/templates/request_builders.py.jinja2 +1 -1
  65. package/autorest/codegen/templates/serialization.py.jinja2 +2006 -0
  66. package/autorest/codegen/templates/setup.py.jinja2 +13 -3
  67. package/autorest/codegen/templates/vendor.py.jinja2 +11 -1
  68. package/autorest/jsonrpc/server.py +15 -3
  69. package/autorest/m4reformatter/__init__.py +1126 -0
  70. package/autorest/multiapi/models/client.py +12 -2
  71. package/autorest/multiapi/models/code_model.py +1 -1
  72. package/autorest/multiapi/serializers/__init__.py +18 -4
  73. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  74. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  75. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  76. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  77. package/autorest/postprocess/__init__.py +202 -0
  78. package/autorest/postprocess/get_all.py +19 -0
  79. package/autorest/postprocess/venvtools.py +73 -0
  80. package/autorest/preprocess/__init__.py +210 -0
  81. package/autorest/preprocess/helpers.py +54 -0
  82. package/autorest/{namer → preprocess}/python_mappings.py +21 -16
  83. package/package.json +2 -2
  84. package/autorest/codegen/models/credential_model.py +0 -55
  85. package/autorest/codegen/models/credential_schema.py +0 -95
  86. package/autorest/codegen/models/credential_schema_policy.py +0 -73
  87. package/autorest/codegen/models/dictionary_schema.py +0 -106
  88. package/autorest/codegen/models/enum_schema.py +0 -225
  89. package/autorest/codegen/models/list_schema.py +0 -135
  90. package/autorest/codegen/models/object_schema.py +0 -303
  91. package/autorest/codegen/models/primitive_schemas.py +0 -495
  92. package/autorest/codegen/models/request_builder_parameter_list.py +0 -249
  93. package/autorest/codegen/models/schema_request.py +0 -55
  94. package/autorest/codegen/models/schema_response.py +0 -141
  95. package/autorest/namer/__init__.py +0 -23
  96. package/autorest/namer/name_converter.py +0 -509
@@ -3,168 +3,85 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- import logging
7
- from enum import Enum
8
-
9
- from typing import Dict, Optional, List, Any, Union, Tuple, cast, TYPE_CHECKING
10
-
11
- from .imports import FileImport, ImportType, TypingSection
6
+ import abc
7
+ from enum import Enum, auto
8
+
9
+ from typing import (
10
+ Dict,
11
+ Any,
12
+ TYPE_CHECKING,
13
+ List,
14
+ Optional,
15
+ TypeVar,
16
+ Union,
17
+ Generic,
18
+ )
19
+
20
+ from .imports import FileImport, ImportType
12
21
  from .base_model import BaseModel
13
- from .base_schema import BaseSchema
14
- from .constant_schema import ConstantSchema
15
- from .object_schema import ObjectSchema
16
- from .property import Property
17
- from .primitive_schemas import IOSchema
18
- from .utils import get_schema
22
+ from .base_type import BaseType
23
+ from .constant_type import ConstantType
24
+ from .utils import add_to_description
19
25
 
20
26
  if TYPE_CHECKING:
21
27
  from .code_model import CodeModel
28
+ from .request_builder_parameter import RequestBuilderBodyParameter
22
29
 
23
30
 
24
- _LOGGER = logging.getLogger(__name__)
25
-
26
- _HIDDEN_KWARGS = ["content_type"]
27
-
31
+ class ParameterLocation(str, Enum):
32
+ HEADER = "header"
33
+ PATH = "path"
34
+ ENDPOINT_PATH = "endpointPath"
35
+ QUERY = "query"
36
+ BODY = "body"
37
+ OTHER = "other"
28
38
 
29
- class ParameterMethodLocation(str, Enum):
30
- POSITIONAL = "positional"
31
- KEYWORD_ONLY = "keyword_only"
32
- KWARG = "kwarg"
33
- HIDDEN_KWARG = "hidden_kwarg"
34
39
 
40
+ class ParameterMethodLocation(Enum):
41
+ POSITIONAL = auto()
42
+ KEYWORD_ONLY = auto()
43
+ KWARG = auto()
35
44
 
36
- class ParameterLocation(Enum):
37
- Path = "path"
38
- Body = "body"
39
- Query = "query"
40
- Header = "header"
41
- Uri = "uri"
42
- Other = "other"
43
45
 
46
+ class ParameterDelimeter(str, Enum):
47
+ SPACE = "space"
48
+ PIPE = "pipe"
49
+ TAB = "tab"
50
+ COMMA = "comma"
44
51
 
45
- class ParameterStyle(Enum):
46
- simple = "simple"
47
- label = "label"
48
- matrix = "matrix"
49
- form = "form"
50
- spaceDelimited = "spaceDelimited"
51
- pipeDelimited = "pipeDelimited"
52
- deepObject = "deepObject"
53
- tabDelimited = "tabDelimited"
54
- json = "json"
55
- binary = "binary"
56
- xml = "xml"
57
- multipart = "multipart"
58
52
 
53
+ class _ParameterBase(
54
+ BaseModel, abc.ABC
55
+ ): # pylint: disable=too-many-instance-attributes
56
+ """Base class for all parameters"""
59
57
 
60
- def get_target_property_name(code_model: "CodeModel", target_property_id: int) -> str:
61
- for obj in code_model.schemas.values():
62
- for prop in obj.properties:
63
- if prop.id == target_property_id:
64
- return prop.name
65
- raise KeyError("Didn't find the target property")
66
-
67
-
68
- class Parameter(
69
- BaseModel
70
- ): # pylint: disable=too-many-instance-attributes, too-many-public-methods
71
58
  def __init__(
72
59
  self,
73
60
  yaml_data: Dict[str, Any],
74
61
  code_model: "CodeModel",
75
- schema: BaseSchema,
76
- rest_api_name: str,
77
- serialized_name: str,
78
- description: str,
79
- implementation: str,
80
- required: bool,
81
- location: ParameterLocation,
82
- skip_url_encoding: bool,
83
- constraints: List[Any],
84
- target_property_name: Optional[
85
- Union[int, str]
86
- ] = None, # first uses id as placeholder
87
- style: Optional[ParameterStyle] = None,
88
- explode: Optional[bool] = False,
89
- *,
90
- flattened: bool = False,
91
- grouped_by: Optional["Parameter"] = None,
92
- original_parameter: Optional["Parameter"] = None,
93
- client_default_value: Optional[Any] = None,
94
- content_types: Optional[List[str]] = None,
62
+ type: BaseType,
95
63
  ) -> None:
96
64
  super().__init__(yaml_data, code_model)
97
- self.code_model = code_model
98
- self.schema = schema
99
- self.rest_api_name = rest_api_name
100
- self.serialized_name = serialized_name
101
- self._description = description
102
- self._implementation = implementation
103
- self.required = required
104
- self.location = location
105
- self.skip_url_encoding = skip_url_encoding
106
- self.constraints = constraints
107
- self.target_property_name = target_property_name
108
- self.style = style
109
- self.explode = explode
110
- self.flattened = flattened
111
- self.grouped_by = grouped_by
112
- self.original_parameter = original_parameter
113
- self.client_default_value = client_default_value
114
- self.has_multiple_content_types: bool = False
115
- self.multiple_content_types_type_annot: Optional[str] = None
116
- self.multiple_content_types_docstring_type: Optional[str] = None
117
- self.is_multipart = (
118
- yaml_data.get("language", {}).get("python", {}).get("multipart", False)
65
+ self.rest_api_name: str = yaml_data["restApiName"]
66
+ self.client_name: str = self.yaml_data["clientName"]
67
+ self.optional: bool = self.yaml_data["optional"]
68
+ self.location: ParameterLocation = self.yaml_data["location"]
69
+ self.client_default_value = self.yaml_data.get("clientDefaultValue", None)
70
+ self.in_docstring = self.yaml_data.get("inDocstring", True)
71
+ self.type = type
72
+ if self.client_default_value is None:
73
+ self.client_default_value = self.type.client_default_value
74
+ # name of grouper if it is grouped by another parameter
75
+ self.grouped_by: Optional[str] = self.yaml_data.get("groupedBy")
76
+ # property matching property name to parameter name for grouping params
77
+ # and flattened body params
78
+ self.property_to_parameter_name: Optional[Dict[str, str]] = self.yaml_data.get(
79
+ "propertyToParameterName"
119
80
  )
120
- self.is_data_input = (
121
- yaml_data.get("isPartialBody", False) and not self.is_multipart
122
- )
123
- self.content_types = content_types or []
124
- self.body_kwargs: List[Parameter] = []
125
- self.is_body_kwarg = False
126
- self.need_import = True
127
- self._method_location: Optional[ParameterMethodLocation] = None
128
-
129
- def __hash__(self) -> int:
130
- return hash(self.serialized_name)
131
-
132
- @property
133
- def description(self):
134
- try:
135
- description = self._description
136
- if self.schema.extra_description_information:
137
- if description:
138
- description += " "
139
- description += f"{self.schema.extra_description_information}"
140
- if isinstance(self.schema, ConstantSchema) and not self.constant:
141
- if description:
142
- description += " "
143
- description += f"Known values are {self.schema.get_declaration(self.schema.value)} or {None}."
144
- if self.has_default_value and not any(
145
- l
146
- for l in ["default value is", "default is"]
147
- if l in description.lower()
148
- ):
149
- description += f" Default value is {self.default_value_declaration}."
150
- if self.constant:
151
- description += " Note that overriding this default value may result in unsupported behavior."
152
- return description
153
- except AttributeError:
154
- pass
155
- return self._description
156
-
157
- @description.setter
158
- def description(self, val: str):
159
- self._description = val
160
-
161
- @property
162
- def is_json_parameter(self) -> bool:
163
- if self.is_multipart or self.is_data_input:
164
- return False
165
- if self.style == ParameterStyle.xml:
166
- return False
167
- return True
81
+ self.flattened: bool = self.yaml_data.get("flattened", False)
82
+ self.in_flattened_body: bool = self.yaml_data.get("inFlattenedBody", False)
83
+ self.grouper: bool = self.yaml_data.get("grouper", False)
84
+ self.check_client_input: bool = self.yaml_data.get("checkClientInput", False)
168
85
 
169
86
  @property
170
87
  def constant(self) -> bool:
@@ -172,325 +89,301 @@ class Parameter(
172
89
  Checking to see if it's required, because if not, we don't consider it
173
90
  a constant because it can have a value of None.
174
91
  """
175
- if isinstance(self.schema, dict):
176
- if not self.schema.get("type") == "constant":
177
- return False
178
- else:
179
- if not isinstance(self.schema, ConstantSchema):
180
- return False
181
- return self.required
92
+ return not self.optional and isinstance(self.type, ConstantType)
182
93
 
183
94
  @property
184
- def constant_declaration(self) -> str:
185
- if self.schema:
186
- if isinstance(self.schema, ConstantSchema):
187
- return self.schema.get_declaration(self.schema.value)
188
- raise ValueError(
189
- "Trying to get constant declaration for a schema that is not ConstantSchema"
95
+ def description(self) -> str:
96
+ base_description = self.yaml_data["description"]
97
+ type_description = self.type.description(is_operation_file=True)
98
+ if type_description:
99
+ base_description = add_to_description(base_description, type_description)
100
+ if self.optional and isinstance(self.type, ConstantType):
101
+ base_description = add_to_description(
102
+ base_description,
103
+ f"Known values are {self.type.get_declaration()} and None.",
104
+ )
105
+ if not (self.optional or self.client_default_value):
106
+ base_description = add_to_description(base_description, "Required.")
107
+ if self.client_default_value is not None:
108
+ base_description = add_to_description(
109
+ base_description,
110
+ f"Default value is {self.client_default_value_declaration}.",
111
+ )
112
+ if self.optional and self.client_default_value is None:
113
+ base_description = add_to_description(
114
+ base_description,
115
+ f"Default value is {self.client_default_value_declaration}.",
190
116
  )
191
- raise ValueError("Trying to get a declaration for a schema that doesn't exist")
117
+ if self.constant:
118
+ base_description = add_to_description(
119
+ base_description,
120
+ "Note that overriding this default value may result in unsupported behavior.",
121
+ )
122
+ return base_description
192
123
 
193
124
  @property
194
- def serialization_formats(self) -> List[str]:
195
- return self.yaml_data.get("serializationFormats", [])
125
+ def client_default_value_declaration(self):
126
+ """Declaration of parameter's client default value"""
127
+ if self.client_default_value is None:
128
+ return None
129
+ return self.type.get_declaration(self.client_default_value)
196
130
 
197
- @property
198
- def xml_serialization_ctxt(self) -> str:
199
- return self.schema.xml_serialization_ctxt() or ""
131
+ def type_annotation(self, **kwargs: Any) -> str:
132
+ kwargs["is_operation_file"] = True
133
+ type_annot = self.type.type_annotation(**kwargs)
134
+ if self.optional and self.client_default_value is None:
135
+ return f"Optional[{type_annot}]"
136
+ return type_annot
200
137
 
201
- @property
202
- def is_body(self) -> bool:
203
- return self.location == ParameterLocation.Body
138
+ def docstring_text(self, **kwargs: Any) -> str:
139
+ return self.type.docstring_text(**kwargs)
204
140
 
205
- @property
206
- def inputtable_by_user(self) -> bool:
207
- return self.rest_api_name != "Accept"
141
+ def docstring_type(self, **kwargs: Any) -> str:
142
+ return self.type.docstring_type(**kwargs)
208
143
 
209
144
  @property
210
- def pre_semicolon_content_types(self) -> List[str]:
211
- """Splits on semicolon of media types and returns the first half.
212
- I.e. ["text/plain; charset=UTF-8"] -> ["text/plain"]
213
- """
214
- return [content_type.split(";")[0] for content_type in self.content_types]
145
+ def serialization_type(self) -> str:
146
+ return self.type.serialization_type
215
147
 
216
- @property
217
- def in_method_signature(self) -> bool:
218
- return not (
219
- # if not inputtable, don't put in signature
220
- not self.inputtable_by_user
221
- # If i'm not in the method code, no point in being in signature
222
- or not self.in_method_code
223
- # If I'm grouped, my grouper will be on signature, not me
224
- or self.grouped_by
225
- # If I'm body and it's flattened, I'm not either
226
- or (self.is_body and self.flattened)
148
+ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
149
+ file_import = FileImport()
150
+ file_import.merge(
151
+ self.type.imports(is_operation_file=True, async_mode=async_mode, **kwargs)
227
152
  )
153
+ if self.optional and self.client_default_value is None:
154
+ file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
155
+ return file_import
228
156
 
229
157
  @property
230
- def corresponding_grouped_property(self) -> Property:
231
- if not self.grouped_by:
232
- raise ValueError("Should only be calling if your parameter is grouped")
233
- try:
234
- return next(
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
- )
240
- )
241
- except StopIteration:
242
- raise ValueError(
243
- "There is not a corresponding grouped property for your parameter."
244
- )
158
+ def method_location(self) -> ParameterMethodLocation:
159
+ raise NotImplementedError("Please implement in children")
245
160
 
246
161
  @property
247
- def in_method_code(self) -> bool:
248
- return self.rest_api_name != "$host"
162
+ def description_keyword(self) -> str:
163
+ return (
164
+ "param"
165
+ if self.method_location == ParameterMethodLocation.POSITIONAL
166
+ else "keyword"
167
+ )
249
168
 
250
169
  @property
251
- def implementation(self) -> str:
252
- # https://github.com/Azure/autorest.modelerfour/issues/81
253
- if self.serialized_name == "api_version":
254
- return "Method"
255
- return self._implementation
170
+ def docstring_type_keyword(self) -> str:
171
+ return (
172
+ "type"
173
+ if self.method_location == ParameterMethodLocation.POSITIONAL
174
+ else "paramtype"
175
+ )
256
176
 
257
177
  @property
258
- def _is_io_json(self):
259
- return any(
260
- k for k in self.body_kwargs if k.serialized_name == "json"
261
- ) and isinstance(self.schema, IOSchema)
262
-
263
- def _default_value(self) -> Tuple[Optional[Any], str, str]:
264
- type_annot = (
265
- self.multiple_content_types_type_annot
266
- or self.schema.type_annotation(is_operation_file=True)
267
- )
268
- if 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:
271
- type_annot = f"Optional[{type_annot}]"
178
+ @abc.abstractmethod
179
+ def in_method_signature(self) -> bool:
180
+ ...
272
181
 
273
- if self.client_default_value is not None:
274
- return (
275
- self.client_default_value,
276
- self.schema.get_declaration(self.client_default_value),
277
- type_annot,
278
- )
182
+ def method_signature(self, is_python3_file: bool, async_mode: bool) -> str:
183
+ type_annot = self.type_annotation(async_mode=async_mode)
184
+ if is_python3_file:
185
+ if self.client_default_value is not None or self.optional:
186
+ return f"{self.client_name}: {type_annot} = {self.client_default_value_declaration},"
187
+ return f"{self.client_name}: {type_annot},"
188
+ if self.client_default_value is not None or self.optional:
189
+ return f"{self.client_name}={self.client_default_value_declaration}, # type: {type_annot}"
190
+ return f"{self.client_name}, # type: {type_annot}"
279
191
 
280
- if self.multiple_content_types_type_annot:
281
- # means this parameter has multiple media types. We force default value to be None.
282
- default_value = None
283
- default_value_declaration = "None"
284
- else:
285
- if isinstance(self.schema, ConstantSchema):
286
- if (
287
- self.required
288
- or self.is_content_type
289
- or not self.code_model.options["default_optional_constants_to_none"]
290
- ):
291
- default_value = self.schema.get_declaration(self.schema.value)
292
- else:
293
- default_value = None
294
- default_value_declaration = default_value
295
- else:
296
- default_value = self.schema.default_value
297
- default_value_declaration = self.schema.default_value_declaration
298
- if default_value is not None and self.required:
299
- _LOGGER.warning(
300
- "Parameter '%s' is required and has a default value, this combination is not recommended",
301
- self.rest_api_name,
302
- )
303
192
 
304
- return default_value, default_value_declaration, type_annot
193
+ class _BodyParameterBase(_ParameterBase):
194
+ """Base class for body parameters"""
305
195
 
306
196
  @property
307
- def description_keyword(self) -> str:
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
- )
197
+ def is_partial_body(self) -> bool:
198
+ """Whether it's part of a bigger body parameter, i.e. a MultipartBodyParameter"""
199
+ return self.yaml_data.get("isPartialBody", False)
318
200
 
319
201
  @property
320
- def docstring_type_keyword(self) -> str:
202
+ def method_location(self) -> ParameterMethodLocation:
321
203
  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"
204
+ ParameterMethodLocation.KWARG
205
+ if self.constant
206
+ else ParameterMethodLocation.POSITIONAL
330
207
  )
331
208
 
332
209
  @property
333
- def default_value(self) -> Optional[Any]:
334
- # exposing default_value because client_default_value doesn't get updated with
335
- # default values we bubble up from the schema
336
- return self._default_value()[0]
210
+ def in_method_signature(self) -> bool:
211
+ return not (self.flattened or self.grouped_by)
337
212
 
338
- @property
339
- def default_value_declaration(self) -> Optional[Any]:
340
- return self._default_value()[1]
341
213
 
342
- def type_annotation(
343
- self, *, is_operation_file: bool = False # pylint: disable=unused-argument
344
- ) -> str:
345
- return self._default_value()[2]
214
+ class BodyParameter(_BodyParameterBase):
215
+ """Body parameter."""
346
216
 
347
217
  @property
348
- def serialization_type(self) -> str:
349
- return self.schema.serialization_type
218
+ def content_types(self) -> List[str]:
219
+ return self.yaml_data["contentTypes"]
350
220
 
351
221
  @property
352
- def docstring_type(self) -> str:
353
- retval = (
354
- self.multiple_content_types_docstring_type or self.schema.docstring_type
222
+ def default_content_type(self) -> str:
223
+ return self.yaml_data["defaultContentType"]
224
+
225
+ @classmethod
226
+ def from_yaml(
227
+ cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
228
+ ) -> "BodyParameter":
229
+ return cls(
230
+ yaml_data=yaml_data,
231
+ code_model=code_model,
232
+ type=code_model.lookup_type(id(yaml_data["type"])),
355
233
  )
356
- if self._is_io_json:
357
- retval += " or Any"
358
- return retval
234
+
235
+
236
+ EntryBodyParameterType = TypeVar(
237
+ "EntryBodyParameterType", bound=Union[BodyParameter, "RequestBuilderBodyParameter"]
238
+ )
239
+
240
+
241
+ class _MultipartBodyParameter(Generic[EntryBodyParameterType], BodyParameter):
242
+ """Base class for MultipartBodyParameter and RequestBuilderMultipartBodyParameter"""
243
+
244
+ def __init__(
245
+ self,
246
+ yaml_data: Dict[str, Any],
247
+ code_model: "CodeModel",
248
+ type: BaseType,
249
+ entries: List[EntryBodyParameterType],
250
+ ) -> None:
251
+ super().__init__(yaml_data, code_model, type)
252
+ self.entries = entries
359
253
 
360
254
  @property
361
- def has_default_value(self):
362
- return self.default_value is not None or not self.required
255
+ def in_method_signature(self) -> bool:
256
+ # Right now, only legacy generates with multipart bodies
257
+ # and legacy generates with the multipart body arguments splatted out
258
+ return False
363
259
 
364
- def method_signature(self, is_python3_file: bool) -> str:
365
- type_annot = self.type_annotation(is_operation_file=True)
366
- if is_python3_file:
367
- if self.has_default_value:
368
- return f"{self.serialized_name}: {type_annot} = {self.default_value_declaration},"
369
- return f"{self.serialized_name}: {type_annot},"
370
- if self.has_default_value:
371
- return f"{self.serialized_name}={self.default_value_declaration}, # type: {type_annot}"
372
- return f"{self.serialized_name}, # type: {type_annot}"
260
+
261
+ class MultipartBodyParameter(
262
+ _MultipartBodyParameter[BodyParameter] # pylint: disable=unsubscriptable-object
263
+ ):
264
+ """Multipart body parameter for Operation. Used for files and data input."""
265
+
266
+ @classmethod
267
+ def from_yaml(
268
+ cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
269
+ ) -> "MultipartBodyParameter":
270
+ return cls(
271
+ yaml_data=yaml_data,
272
+ code_model=code_model,
273
+ type=code_model.lookup_type(id(yaml_data["type"])),
274
+ entries=[
275
+ BodyParameter.from_yaml(entry, code_model)
276
+ for entry in yaml_data["entries"]
277
+ ],
278
+ )
279
+
280
+
281
+ class Parameter(_ParameterBase):
282
+ """Basic Parameter class"""
283
+
284
+ def __init__(
285
+ self,
286
+ yaml_data: Dict[str, Any],
287
+ code_model: "CodeModel",
288
+ type: BaseType,
289
+ ) -> None:
290
+ super().__init__(yaml_data, code_model, type=type)
291
+
292
+ self.implementation: str = yaml_data["implementation"]
293
+ self.skip_url_encoding: bool = self.yaml_data.get("skipUrlEncoding", False)
294
+ self.explode: bool = self.yaml_data.get("explode", False)
295
+ self.in_overload: bool = self.yaml_data["inOverload"]
296
+ self.in_overriden: bool = self.yaml_data.get("inOverriden", False)
297
+ self.delimiter: Optional[ParameterDelimeter] = self.yaml_data.get("delimiter")
373
298
 
374
299
  @property
375
- def full_serialized_name(self) -> str:
376
- origin_name = self.serialized_name
300
+ def in_method_signature(self) -> bool:
301
+ return not (self.rest_api_name == "Accept" or self.grouped_by or self.flattened)
302
+
303
+ @property
304
+ def full_client_name(self) -> str:
377
305
  if self.implementation == "Client":
378
- origin_name = f"self._config.{self.serialized_name}"
379
- return origin_name
306
+ return f"self._config.{self.client_name}"
307
+ return self.client_name
380
308
 
381
309
  @property
382
- def is_content_type(self) -> bool:
383
- return (
384
- self.rest_api_name == "Content-Type"
385
- and self.location == ParameterLocation.Header
386
- )
310
+ def xml_serialization_ctxt(self) -> str:
311
+ return self.type.xml_serialization_ctxt or ""
387
312
 
388
313
  @property
389
314
  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:
315
+ if not self.in_method_signature:
316
+ raise ValueError(f"Parameter '{self.client_name}' is not in the method.")
317
+ if self.grouper:
318
+ return ParameterMethodLocation.POSITIONAL
319
+ if self.constant:
320
+ return ParameterMethodLocation.KWARG
321
+ if self.rest_api_name == "Content-Type":
322
+ if self.in_overload:
323
+ return ParameterMethodLocation.KEYWORD_ONLY
397
324
  return ParameterMethodLocation.KWARG
325
+ query_or_header = self.location in (
326
+ ParameterLocation.HEADER,
327
+ ParameterLocation.QUERY,
328
+ )
329
+ if (
330
+ self.code_model.options["only_path_and_body_params_positional"]
331
+ and query_or_header
332
+ ):
333
+ return ParameterMethodLocation.KEYWORD_ONLY
398
334
  return ParameterMethodLocation.POSITIONAL
399
335
 
400
- @method_location.setter
401
- def method_location(self, val: ParameterMethodLocation) -> None:
402
- self._method_location = val
403
-
404
336
  @classmethod
405
- def from_yaml(
406
- cls,
407
- yaml_data: Dict[str, Any],
408
- code_model: "CodeModel",
409
- *,
410
- content_types: Optional[List[str]] = None,
411
- ) -> "Parameter":
412
- http_protocol = yaml_data["protocol"].get(
413
- "http", {"in": ParameterLocation.Other}
414
- )
415
- serialized_name = yaml_data["language"]["python"]["name"]
416
- schema = get_schema(code_model, yaml_data.get("schema"), serialized_name)
417
- target_property = yaml_data.get("targetProperty")
418
- target_property_name = (
419
- get_target_property_name(code_model, id(target_property))
420
- if target_property
421
- else None
422
- )
423
-
337
+ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):
424
338
  return cls(
425
339
  yaml_data=yaml_data,
426
340
  code_model=code_model,
427
- schema=schema, # FIXME replace by operation model
428
- # See also https://github.com/Azure/autorest.modelerfour/issues/80
429
- rest_api_name=yaml_data["language"]["default"].get(
430
- "serializedName", yaml_data["language"]["default"]["name"]
431
- ),
432
- serialized_name=serialized_name,
433
- description=yaml_data["language"]["python"]["description"],
434
- implementation=yaml_data["implementation"],
435
- required=yaml_data.get("required", False),
436
- location=ParameterLocation(http_protocol["in"]),
437
- skip_url_encoding=yaml_data.get("extensions", {}).get(
438
- "x-ms-skip-url-encoding", False
439
- ),
440
- constraints=[], # FIXME constraints
441
- target_property_name=target_property_name,
442
- style=ParameterStyle(http_protocol["style"])
443
- if "style" in http_protocol
444
- else None,
445
- explode=http_protocol.get("explode", False),
446
- grouped_by=yaml_data.get("groupedBy", None),
447
- original_parameter=yaml_data.get("originalParameter", None),
448
- flattened=yaml_data.get("flattened", False),
449
- client_default_value=yaml_data.get("clientDefaultValue"),
450
- content_types=content_types,
341
+ type=code_model.lookup_type(id(yaml_data["type"])),
451
342
  )
452
343
 
453
- def imports(self) -> FileImport:
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
- )
464
344
 
465
- return file_import
466
- return FileImport()
345
+ class ClientParameter(Parameter):
346
+ """Client parameter"""
467
347
 
348
+ @property
349
+ def is_host(self) -> bool:
350
+ return self.rest_api_name == "$host"
468
351
 
469
- class ParameterOnlyPathAndBodyPositional(Parameter):
470
352
  @property
471
353
  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,
354
+ if self.constant:
355
+ return ParameterMethodLocation.KWARG
356
+ if self.is_host and (
357
+ self.code_model.options["version_tolerant"]
358
+ or self.code_model.options["low_level_client"]
484
359
  ):
360
+ # this means i am the base url
485
361
  return ParameterMethodLocation.KEYWORD_ONLY
486
- return super_method_location
362
+ return ParameterMethodLocation.POSITIONAL
363
+
364
+
365
+ class ConfigParameter(Parameter):
366
+ """Config Parameter"""
367
+
368
+ @property
369
+ def in_method_signature(self) -> bool:
370
+ return not self.is_host
371
+
372
+ @property
373
+ def is_host(self) -> bool:
374
+ return self.rest_api_name == "$host"
487
375
 
488
- @method_location.setter
489
- def method_location(self, val: ParameterMethodLocation) -> None:
490
- self._method_location = val
376
+ @property
377
+ def method_location(self) -> ParameterMethodLocation:
378
+ if self.constant:
379
+ return ParameterMethodLocation.KWARG
380
+ return ParameterMethodLocation.POSITIONAL
491
381
 
492
382
 
493
- def get_parameter(code_model):
494
- if code_model.options["only_path_and_body_params_positional"]:
495
- return ParameterOnlyPathAndBodyPositional
496
- return Parameter
383
+ def get_body_parameter(
384
+ yaml_data: Dict[str, Any], code_model: "CodeModel"
385
+ ) -> Union[BodyParameter, MultipartBodyParameter]:
386
+ """Creates a regular body parameter or Multipart body parameter"""
387
+ if yaml_data.get("entries"):
388
+ return MultipartBodyParameter.from_yaml(yaml_data, code_model)
389
+ return BodyParameter.from_yaml(yaml_data, code_model)