@autorest/python 5.14.0 → 5.17.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 (120) hide show
  1. package/ChangeLog.md +91 -2
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +2 -3
  4. package/autorest/black/__init__.py +12 -5
  5. package/autorest/codegen/__init__.py +130 -179
  6. package/autorest/codegen/models/__init__.py +122 -78
  7. package/autorest/codegen/models/base_builder.py +70 -72
  8. package/autorest/codegen/models/base_model.py +7 -5
  9. package/autorest/codegen/models/{base_schema.py → base_type.py} +62 -49
  10. package/autorest/codegen/models/client.py +195 -36
  11. package/autorest/codegen/models/code_model.py +165 -299
  12. package/autorest/codegen/models/combined_type.py +107 -0
  13. package/autorest/codegen/models/constant_type.py +122 -0
  14. package/autorest/codegen/models/credential_types.py +224 -0
  15. package/autorest/codegen/models/dictionary_type.py +116 -0
  16. package/autorest/codegen/models/enum_type.py +195 -0
  17. package/autorest/codegen/models/imports.py +95 -41
  18. package/autorest/codegen/models/list_type.py +134 -0
  19. package/autorest/codegen/models/lro_operation.py +90 -133
  20. package/autorest/codegen/models/lro_paging_operation.py +28 -12
  21. package/autorest/codegen/models/model_type.py +239 -0
  22. package/autorest/codegen/models/operation.py +415 -241
  23. package/autorest/codegen/models/operation_group.py +82 -88
  24. package/autorest/codegen/models/paging_operation.py +101 -117
  25. package/autorest/codegen/models/parameter.py +307 -322
  26. package/autorest/codegen/models/parameter_list.py +366 -357
  27. package/autorest/codegen/models/primitive_types.py +544 -0
  28. package/autorest/codegen/models/property.py +122 -134
  29. package/autorest/codegen/models/request_builder.py +138 -86
  30. package/autorest/codegen/models/request_builder_parameter.py +122 -79
  31. package/autorest/codegen/models/response.py +325 -0
  32. package/autorest/codegen/models/utils.py +17 -1
  33. package/autorest/codegen/serializers/__init__.py +242 -118
  34. package/autorest/codegen/serializers/builder_serializer.py +863 -1027
  35. package/autorest/codegen/serializers/client_serializer.py +148 -82
  36. package/autorest/codegen/serializers/general_serializer.py +44 -47
  37. package/autorest/codegen/serializers/import_serializer.py +96 -31
  38. package/autorest/codegen/serializers/metadata_serializer.py +39 -79
  39. package/autorest/codegen/serializers/model_base_serializer.py +65 -29
  40. package/autorest/codegen/serializers/model_generic_serializer.py +9 -10
  41. package/autorest/codegen/serializers/model_init_serializer.py +4 -2
  42. package/autorest/codegen/serializers/model_python3_serializer.py +29 -22
  43. package/autorest/codegen/serializers/operation_groups_serializer.py +21 -18
  44. package/autorest/codegen/serializers/operations_init_serializer.py +23 -11
  45. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  46. package/autorest/codegen/serializers/patch_serializer.py +14 -2
  47. package/autorest/codegen/serializers/request_builders_serializer.py +57 -0
  48. package/autorest/codegen/serializers/utils.py +0 -103
  49. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  50. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -7
  51. package/autorest/codegen/templates/config.py.jinja2 +13 -13
  52. package/autorest/codegen/templates/enum.py.jinja2 +4 -4
  53. package/autorest/codegen/templates/enum_container.py.jinja2 +1 -2
  54. package/autorest/codegen/templates/init.py.jinja2 +9 -6
  55. package/autorest/codegen/templates/keywords.jinja2 +14 -1
  56. package/autorest/codegen/templates/lro_operation.py.jinja2 +6 -5
  57. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +6 -5
  58. package/autorest/codegen/templates/metadata.json.jinja2 +36 -35
  59. package/autorest/codegen/templates/model.py.jinja2 +23 -29
  60. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  61. package/autorest/codegen/templates/model_init.py.jinja2 +9 -8
  62. package/autorest/codegen/templates/operation.py.jinja2 +10 -15
  63. package/autorest/codegen/templates/operation_group.py.jinja2 +14 -13
  64. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -2
  65. package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
  66. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
  67. package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
  68. package/autorest/codegen/templates/patch.py.jinja2 +18 -29
  69. package/autorest/codegen/templates/request_builder.py.jinja2 +20 -13
  70. package/autorest/codegen/templates/setup.py.jinja2 +9 -3
  71. package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
  72. package/autorest/jsonrpc/__init__.py +7 -12
  73. package/autorest/jsonrpc/localapi.py +4 -3
  74. package/autorest/jsonrpc/server.py +28 -9
  75. package/autorest/jsonrpc/stdstream.py +13 -6
  76. package/autorest/m2r/__init__.py +5 -8
  77. package/autorest/m4reformatter/__init__.py +1108 -0
  78. package/autorest/multiapi/__init__.py +24 -14
  79. package/autorest/multiapi/models/client.py +21 -11
  80. package/autorest/multiapi/models/code_model.py +23 -10
  81. package/autorest/multiapi/models/config.py +4 -1
  82. package/autorest/multiapi/models/constant_global_parameter.py +1 -0
  83. package/autorest/multiapi/models/global_parameter.py +2 -1
  84. package/autorest/multiapi/models/global_parameters.py +14 -8
  85. package/autorest/multiapi/models/imports.py +35 -18
  86. package/autorest/multiapi/models/mixin_operation.py +5 -5
  87. package/autorest/multiapi/models/operation_group.py +2 -1
  88. package/autorest/multiapi/models/operation_mixin_group.py +21 -10
  89. package/autorest/multiapi/serializers/__init__.py +20 -25
  90. package/autorest/multiapi/serializers/import_serializer.py +47 -15
  91. package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
  92. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  93. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  94. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  95. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  96. package/autorest/multiapi/utils.py +3 -3
  97. package/autorest/postprocess/__init__.py +202 -0
  98. package/autorest/postprocess/get_all.py +19 -0
  99. package/autorest/postprocess/venvtools.py +73 -0
  100. package/autorest/preprocess/__init__.py +209 -0
  101. package/autorest/preprocess/helpers.py +54 -0
  102. package/autorest/{namer → preprocess}/python_mappings.py +25 -32
  103. package/package.json +3 -3
  104. package/run-python3.js +2 -3
  105. package/venvtools.py +1 -1
  106. package/autorest/codegen/models/constant_schema.py +0 -97
  107. package/autorest/codegen/models/credential_schema.py +0 -90
  108. package/autorest/codegen/models/credential_schema_policy.py +0 -77
  109. package/autorest/codegen/models/dictionary_schema.py +0 -103
  110. package/autorest/codegen/models/enum_schema.py +0 -246
  111. package/autorest/codegen/models/list_schema.py +0 -113
  112. package/autorest/codegen/models/object_schema.py +0 -249
  113. package/autorest/codegen/models/primitive_schemas.py +0 -476
  114. package/autorest/codegen/models/request_builder_parameter_list.py +0 -280
  115. package/autorest/codegen/models/rest.py +0 -42
  116. package/autorest/codegen/models/schema_request.py +0 -45
  117. package/autorest/codegen/models/schema_response.py +0 -123
  118. package/autorest/codegen/serializers/rest_serializer.py +0 -57
  119. package/autorest/namer/__init__.py +0 -25
  120. package/autorest/namer/name_converter.py +0 -412
@@ -3,141 +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
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
22
+ from .base_type import BaseType
23
+ from .constant_type import ConstantType
24
+ from .utils import add_to_description
25
+
26
+ if TYPE_CHECKING:
27
+ from .code_model import CodeModel
28
+ from .request_builder_parameter import RequestBuilderBodyParameter
18
29
 
19
30
 
20
- _LOGGER = logging.getLogger(__name__)
31
+ class ParameterLocation(str, Enum):
32
+ HEADER = "header"
33
+ PATH = "path"
34
+ ENDPOINT_PATH = "endpointPath"
35
+ QUERY = "query"
36
+ BODY = "body"
37
+ OTHER = "other"
21
38
 
22
- _HIDDEN_KWARGS = ["content_type"]
23
39
 
40
+ class ParameterMethodLocation(Enum):
41
+ POSITIONAL = auto()
42
+ KEYWORD_ONLY = auto()
43
+ KWARG = auto()
24
44
 
25
- class ParameterLocation(Enum):
26
- Path = "path"
27
- Body = "body"
28
- Query = "query"
29
- Header = "header"
30
- Uri = "uri"
31
- Other = "other"
32
45
 
46
+ class ParameterDelimeter(str, Enum):
47
+ SPACE = "space"
48
+ PIPE = "pipe"
49
+ TAB = "tab"
50
+ COMMA = "comma"
33
51
 
34
- class ParameterStyle(Enum):
35
- simple = "simple"
36
- label = "label"
37
- matrix = "matrix"
38
- form = "form"
39
- spaceDelimited = "spaceDelimited"
40
- pipeDelimited = "pipeDelimited"
41
- deepObject = "deepObject"
42
- tabDelimited = "tabDelimited"
43
- json = "json"
44
- binary = "binary"
45
- xml = "xml"
46
- multipart = "multipart"
47
52
 
53
+ class _ParameterBase(
54
+ BaseModel, abc.ABC
55
+ ): # pylint: disable=too-many-instance-attributes
56
+ """Base class for all parameters"""
48
57
 
49
- class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too-many-public-methods
50
58
  def __init__(
51
59
  self,
52
- code_model,
53
60
  yaml_data: Dict[str, Any],
54
- schema: BaseSchema,
55
- rest_api_name: str,
56
- serialized_name: str,
57
- description: str,
58
- implementation: str,
59
- required: bool,
60
- location: ParameterLocation,
61
- skip_url_encoding: bool,
62
- constraints: List[Any],
63
- target_property_name: Optional[Union[int, str]] = None, # first uses id as placeholder
64
- style: Optional[ParameterStyle] = None,
65
- explode: Optional[bool] = False,
66
- *,
67
- flattened: bool = False,
68
- grouped_by: Optional["Parameter"] = None,
69
- original_parameter: Optional["Parameter"] = None,
70
- client_default_value: Optional[Any] = None,
71
- keyword_only: Optional[bool] = None,
72
- content_types: Optional[List[str]] = None,
61
+ code_model: "CodeModel",
62
+ type: BaseType,
73
63
  ) -> None:
74
- super().__init__(yaml_data)
75
- self.code_model = code_model
76
- self.schema = schema
77
- self.rest_api_name = rest_api_name
78
- self.serialized_name = serialized_name
79
- self._description = description
80
- self._implementation = implementation
81
- self.required = required
82
- self.location = location
83
- self.skip_url_encoding = skip_url_encoding
84
- self.constraints = constraints
85
- self.target_property_name = target_property_name
86
- self.style = style
87
- self.explode = explode
88
- self.flattened = flattened
89
- self.grouped_by = grouped_by
90
- self.original_parameter = original_parameter
91
- self.client_default_value = client_default_value
92
- self.has_multiple_content_types: bool = False
93
- self.multiple_content_types_type_annot: Optional[str] = None
94
- self.multiple_content_types_docstring_type: Optional[str] = None
95
- self._keyword_only = keyword_only
96
- self.is_multipart = yaml_data.get("language", {}).get("python", {}).get("multipart", False)
97
- self.is_data_input = yaml_data.get("isPartialBody", False) and not self.is_multipart
98
- self.content_types = content_types or []
99
- self.body_kwargs: List[Parameter] = []
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"))
103
-
104
- def __hash__(self) -> int:
105
- return hash(self.serialized_name)
106
-
107
- @property
108
- def description(self):
109
- try:
110
- description = self._description
111
- if self.schema.extra_description_information:
112
- if description:
113
- description += " "
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}."
123
- if self.constant:
124
- description += " Note that overriding this default value may result in unsupported behavior."
125
- return description
126
- except AttributeError:
127
- pass
128
- return self._description
129
-
130
- @description.setter
131
- def description(self, val: str):
132
- self._description = val
133
-
134
- @property
135
- def is_json_parameter(self) -> bool:
136
- if self.is_multipart or self.is_data_input:
137
- return False
138
- if self.style == ParameterStyle.xml:
139
- return False
140
- return True
64
+ super().__init__(yaml_data, code_model)
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"
80
+ )
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)
141
85
 
142
86
  @property
143
87
  def constant(self) -> bool:
@@ -145,260 +89,301 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
145
89
  Checking to see if it's required, because if not, we don't consider it
146
90
  a constant because it can have a value of None.
147
91
  """
148
- if isinstance(self.schema, dict):
149
- if not self.schema.get("type") == "constant":
150
- return False
151
- else:
152
- if not isinstance(self.schema, ConstantSchema):
153
- return False
154
- return self.required
92
+ return not self.optional and isinstance(self.type, ConstantType)
155
93
 
156
94
  @property
157
- def constant_declaration(self) -> str:
158
- if self.schema:
159
- if isinstance(self.schema, ConstantSchema):
160
- return self.schema.get_declaration(self.schema.value)
161
- raise ValueError(
162
- "Trying to get constant declaration for a schema that is not ConstantSchema"
163
- )
164
- raise ValueError("Trying to get a declaration for a schema that doesn't exist")
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}.",
116
+ )
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
165
123
 
166
124
  @property
167
- def serialization_formats(self) -> List[str]:
168
- 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)
169
130
 
170
- @property
171
- def xml_serialization_ctxt(self) -> str:
172
- 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
173
137
 
174
- @property
175
- def is_body(self) -> bool:
176
- return self.location == ParameterLocation.Body
138
+ def docstring_text(self, **kwargs: Any) -> str:
139
+ return self.type.docstring_text(**kwargs)
177
140
 
178
- @property
179
- def pre_semicolon_content_types(self) -> List[str]:
180
- """Splits on semicolon of media types and returns the first half.
181
- I.e. ["text/plain; charset=UTF-8"] -> ["text/plain"]
182
- """
183
- return [content_type.split(";")[0] for content_type in self.content_types]
141
+ def docstring_type(self, **kwargs: Any) -> str:
142
+ return self.type.docstring_type(**kwargs)
184
143
 
185
144
  @property
186
- def in_method_signature(self) -> bool:
187
- return not(
188
- # don't put accept in signature
189
- self.rest_api_name == "Accept"
190
- # If i'm not in the method code, no point in being in signature
191
- or not self.in_method_code
192
- # If I'm grouped, my grouper will be on signature, not me
193
- or self.grouped_by
194
- # If I'm body and it's flattened, I'm not either
195
- or (self.is_body and self.flattened)
145
+ def serialization_type(self) -> str:
146
+ return self.type.serialization_type
147
+
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)
196
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
197
156
 
198
157
  @property
199
- def corresponding_grouped_property(self) -> Property:
200
- if not self.grouped_by:
201
- raise ValueError("Should only be calling if your parameter is grouped")
202
- try:
203
- return next(
204
- p for p in cast(ObjectSchema, self.grouped_by.schema).properties
205
- if any(op for op in p.yaml_data['originalParameter'] if id(op) == self.id)
206
- )
207
- except StopIteration:
208
- raise ValueError("There is not a corresponding grouped property for your parameter.")
158
+ def method_location(self) -> ParameterMethodLocation:
159
+ raise NotImplementedError("Please implement in children")
209
160
 
210
161
  @property
211
- def in_method_code(self) -> bool:
212
- 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
+ )
213
168
 
214
169
  @property
215
- def implementation(self) -> str:
216
- # https://github.com/Azure/autorest.modelerfour/issues/81
217
- if self.serialized_name == "api_version":
218
- return "Method"
219
- 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
+ )
220
176
 
221
177
  @property
222
- def _is_io_json(self):
223
- return any(
224
- k for k in self.body_kwargs if k.serialized_name == "json"
225
- ) and isinstance(self.schema, IOSchema)
226
-
227
- def _default_value(self) -> Tuple[Optional[Any], str, str]:
228
- type_annot = self.multiple_content_types_type_annot or self.schema.operation_type_annotation
229
- if self._is_io_json:
230
- type_annot = f"Union[{type_annot}, JSONType]"
231
- any_types = ["Any", "JSONType"]
232
- if not self.required and type_annot not in any_types and not self._is_io_json:
233
- type_annot = f"Optional[{type_annot}]"
178
+ @abc.abstractmethod
179
+ def in_method_signature(self) -> bool:
180
+ ...
234
181
 
235
- if self.client_default_value is not None:
236
- return self.client_default_value, self.schema.get_declaration(self.client_default_value), type_annot
237
-
238
- if self.multiple_content_types_type_annot:
239
- # means this parameter has multiple media types. We force default value to be None.
240
- default_value = None
241
- default_value_declaration = "None"
242
- else:
243
- if isinstance(self.schema, ConstantSchema):
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
250
- default_value_declaration = default_value
251
- else:
252
- default_value = self.schema.default_value
253
- default_value_declaration = self.schema.default_value_declaration
254
- if default_value is not None and self.required:
255
- _LOGGER.warning(
256
- "Parameter '%s' is required and has a default value, this combination is not recommended",
257
- self.rest_api_name
258
- )
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}"
259
191
 
260
- return default_value, default_value_declaration, type_annot
261
192
 
262
- @property
263
- def description_keyword(self) -> str:
264
- return "keyword" if self.is_kwarg or self.is_keyword_only else "param"
193
+ class _BodyParameterBase(_ParameterBase):
194
+ """Base class for body parameters"""
265
195
 
266
196
  @property
267
- def docstring_type_keyword(self) -> str:
268
- return "paramtype" if self.is_kwarg or self.is_keyword_only else "type"
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)
269
200
 
270
201
  @property
271
- def default_value(self) -> Optional[Any]:
272
- # exposing default_value because client_default_value doesn't get updated with
273
- # default values we bubble up from the schema
274
- return self._default_value()[0]
202
+ def method_location(self) -> ParameterMethodLocation:
203
+ return (
204
+ ParameterMethodLocation.KWARG
205
+ if self.constant
206
+ else ParameterMethodLocation.POSITIONAL
207
+ )
275
208
 
276
209
  @property
277
- def default_value_declaration(self) -> Optional[Any]:
278
- return self._default_value()[1]
210
+ def in_method_signature(self) -> bool:
211
+ return not (self.flattened or self.grouped_by)
279
212
 
280
- @property
281
- def type_annotation(self) -> str:
282
- return self._default_value()[2]
283
213
 
284
- @property
285
- def serialization_type(self) -> str:
286
- return self.schema.serialization_type
214
+ class BodyParameter(_BodyParameterBase):
215
+ """Body parameter."""
287
216
 
288
217
  @property
289
- def docstring_type(self) -> str:
290
- retval = self.multiple_content_types_docstring_type or self.schema.docstring_type
291
- if self._is_io_json:
292
- retval += " or JSONType"
293
- return retval
218
+ def content_types(self) -> List[str]:
219
+ return self.yaml_data["contentTypes"]
294
220
 
295
221
  @property
296
- def has_default_value(self):
297
- return self.default_value is not None or not self.required
222
+ def default_content_type(self) -> str:
223
+ return self.yaml_data["defaultContentType"]
298
224
 
299
- def method_signature(self, is_python3_file: bool) -> str:
300
- if is_python3_file:
301
- if self.has_default_value:
302
- return f"{self.serialized_name}: {self.type_annotation} = {self.default_value_declaration},"
303
- return f"{self.serialized_name}: {self.type_annotation},"
304
- if self.has_default_value:
305
- return f"{self.serialized_name}={self.default_value_declaration}, # type: {self.type_annotation}"
306
- return f"{self.serialized_name}, # type: {self.type_annotation}"
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"])),
233
+ )
307
234
 
308
- @property
309
- def full_serialized_name(self) -> str:
310
- origin_name = self.serialized_name
311
- if self.implementation == "Client":
312
- origin_name = f"self._config.{self.serialized_name}"
313
- return origin_name
314
235
 
315
- @property
316
- def is_keyword_only(self) -> bool:
317
- # this means in async mode, I am documented like def hello(positional_1, *, me!)
318
- return self._keyword_only or False
236
+ EntryBodyParameterType = TypeVar(
237
+ "EntryBodyParameterType", bound=Union[BodyParameter, "RequestBuilderBodyParameter"]
238
+ )
239
+
319
240
 
320
- @is_keyword_only.setter
321
- def is_keyword_only(self, val: bool) -> None:
322
- self._keyword_only = val
323
- self.is_kwarg = False
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
324
253
 
325
254
  @property
326
- def is_hidden(self) -> bool:
327
- return self.serialized_name in _HIDDEN_KWARGS and self.is_kwarg or (
328
- self.yaml_data["implementation"] == "Client" and self.constant
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
259
+
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
+ ],
329
278
  )
330
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")
298
+
299
+ @property
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:
305
+ if self.implementation == "Client":
306
+ return f"self._config.{self.client_name}"
307
+ return self.client_name
308
+
331
309
  @property
332
- def is_content_type(self) -> bool:
333
- return self.rest_api_name == "Content-Type" and self.location == ParameterLocation.Header
310
+ def xml_serialization_ctxt(self) -> str:
311
+ return self.type.xml_serialization_ctxt or ""
334
312
 
335
313
  @property
336
- def is_positional(self) -> bool:
337
- return self.in_method_signature and not (self.is_keyword_only or self.is_kwarg)
314
+ def method_location(self) -> ParameterMethodLocation:
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
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
334
+ return ParameterMethodLocation.POSITIONAL
338
335
 
339
336
  @classmethod
340
- def from_yaml(
341
- cls,
342
- yaml_data: Dict[str, Any],
343
- *,
344
- code_model,
345
- content_types: Optional[List[str]] = None
346
- ) -> "Parameter":
347
- http_protocol = yaml_data["protocol"].get("http", {"in": ParameterLocation.Other})
337
+ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):
348
338
  return cls(
349
- code_model=code_model,
350
339
  yaml_data=yaml_data,
351
- schema=yaml_data.get("schema", None), # FIXME replace by operation model
352
- # See also https://github.com/Azure/autorest.modelerfour/issues/80
353
- rest_api_name=yaml_data["language"]["default"].get(
354
- "serializedName", yaml_data["language"]["default"]["name"]
355
- ),
356
- serialized_name=yaml_data["language"]["python"]["name"],
357
- description=yaml_data["language"]["python"]["description"],
358
- implementation=yaml_data["implementation"],
359
- required=yaml_data.get("required", False),
360
- location=ParameterLocation(http_protocol["in"]),
361
- skip_url_encoding=yaml_data.get("extensions", {}).get("x-ms-skip-url-encoding", False),
362
- constraints=[], # FIXME constraints
363
- target_property_name=id(yaml_data["targetProperty"]) if yaml_data.get("targetProperty") else None,
364
- style=ParameterStyle(http_protocol["style"]) if "style" in http_protocol else None,
365
- explode=http_protocol.get("explode", False),
366
- grouped_by=yaml_data.get("groupedBy", None),
367
- original_parameter=yaml_data.get("originalParameter", None),
368
- flattened=yaml_data.get("flattened", False),
369
- client_default_value=yaml_data.get("clientDefaultValue"),
370
- content_types=content_types
340
+ code_model=code_model,
341
+ type=code_model.lookup_type(id(yaml_data["type"])),
371
342
  )
372
343
 
373
- def imports(self) -> FileImport:
374
- file_import = self.schema.imports()
375
- if not self.required:
376
- file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
377
- if self.has_multiple_content_types or self._is_io_json:
378
- file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
379
344
 
380
- return file_import
345
+ class ClientParameter(Parameter):
346
+ """Client parameter"""
381
347
 
382
- class ParameterOnlyPathAndBodyPositional(Parameter):
348
+ @property
349
+ def is_host(self) -> bool:
350
+ return self.rest_api_name == "$host"
383
351
 
384
352
  @property
385
- def is_keyword_only(self) -> bool:
386
- if self._keyword_only is not None:
387
- return self._keyword_only
388
- return self.in_method_signature and not (
389
- self.is_hidden or
390
- self.location == ParameterLocation.Path or
391
- self.location == ParameterLocation.Uri or
392
- self.location == ParameterLocation.Body or
393
- self.is_kwarg
394
- )
353
+ def method_location(self) -> ParameterMethodLocation:
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"]
359
+ ):
360
+ # this means i am the base url
361
+ return ParameterMethodLocation.KEYWORD_ONLY
362
+ return ParameterMethodLocation.POSITIONAL
363
+
395
364
 
396
- @is_keyword_only.setter
397
- def is_keyword_only(self, val: bool) -> None:
398
- self._keyword_only = val
399
- self.is_kwarg = False
365
+ class ConfigParameter(Parameter):
366
+ """Config Parameter"""
400
367
 
401
- def get_parameter(code_model):
402
- if code_model.options["only_path_and_body_params_positional"]:
403
- return ParameterOnlyPathAndBodyPositional
404
- return Parameter
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"
375
+
376
+ @property
377
+ def method_location(self) -> ParameterMethodLocation:
378
+ if self.constant:
379
+ return ParameterMethodLocation.KWARG
380
+ return ParameterMethodLocation.POSITIONAL
381
+
382
+
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)