@autorest/python 5.15.0 → 5.18.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 (118) hide show
  1. package/ChangeLog.md +98 -4
  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 +122 -211
  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} +68 -45
  10. package/autorest/codegen/models/client.py +193 -40
  11. package/autorest/codegen/models/code_model.py +145 -245
  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 +131 -0
  16. package/autorest/codegen/models/enum_type.py +195 -0
  17. package/autorest/codegen/models/imports.py +93 -41
  18. package/autorest/codegen/models/list_type.py +149 -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 +262 -0
  22. package/autorest/codegen/models/operation.py +412 -259
  23. package/autorest/codegen/models/operation_group.py +80 -91
  24. package/autorest/codegen/models/paging_operation.py +101 -117
  25. package/autorest/codegen/models/parameter.py +302 -341
  26. package/autorest/codegen/models/parameter_list.py +373 -357
  27. package/autorest/codegen/models/primitive_types.py +544 -0
  28. package/autorest/codegen/models/property.py +136 -134
  29. package/autorest/codegen/models/request_builder.py +138 -86
  30. package/autorest/codegen/models/request_builder_parameter.py +122 -86
  31. package/autorest/codegen/models/response.py +325 -0
  32. package/autorest/codegen/models/utils.py +13 -17
  33. package/autorest/codegen/serializers/__init__.py +212 -112
  34. package/autorest/codegen/serializers/builder_serializer.py +931 -1040
  35. package/autorest/codegen/serializers/client_serializer.py +140 -84
  36. package/autorest/codegen/serializers/general_serializer.py +26 -50
  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 +62 -34
  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 -19
  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 +4 -1
  47. package/autorest/codegen/serializers/request_builders_serializer.py +57 -0
  48. package/autorest/codegen/serializers/utils.py +0 -126
  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 -1
  54. package/autorest/codegen/templates/init.py.jinja2 +3 -3
  55. package/autorest/codegen/templates/lro_operation.py.jinja2 +6 -5
  56. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +6 -5
  57. package/autorest/codegen/templates/metadata.json.jinja2 +36 -35
  58. package/autorest/codegen/templates/model.py.jinja2 +23 -24
  59. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  60. package/autorest/codegen/templates/model_init.py.jinja2 +3 -5
  61. package/autorest/codegen/templates/operation.py.jinja2 +10 -14
  62. package/autorest/codegen/templates/operation_group.py.jinja2 +9 -15
  63. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
  64. package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
  65. package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
  66. package/autorest/codegen/templates/request_builder.py.jinja2 +19 -10
  67. package/autorest/codegen/templates/setup.py.jinja2 +9 -3
  68. package/autorest/codegen/templates/vendor.py.jinja2 +1 -1
  69. package/autorest/jsonrpc/__init__.py +7 -12
  70. package/autorest/jsonrpc/localapi.py +4 -3
  71. package/autorest/jsonrpc/server.py +28 -9
  72. package/autorest/jsonrpc/stdstream.py +13 -6
  73. package/autorest/m2r/__init__.py +5 -8
  74. package/autorest/m4reformatter/__init__.py +1126 -0
  75. package/autorest/multiapi/__init__.py +24 -14
  76. package/autorest/multiapi/models/client.py +21 -11
  77. package/autorest/multiapi/models/code_model.py +23 -10
  78. package/autorest/multiapi/models/config.py +4 -1
  79. package/autorest/multiapi/models/constant_global_parameter.py +1 -0
  80. package/autorest/multiapi/models/global_parameter.py +2 -1
  81. package/autorest/multiapi/models/global_parameters.py +14 -8
  82. package/autorest/multiapi/models/imports.py +24 -17
  83. package/autorest/multiapi/models/mixin_operation.py +5 -5
  84. package/autorest/multiapi/models/operation_group.py +2 -1
  85. package/autorest/multiapi/models/operation_mixin_group.py +21 -10
  86. package/autorest/multiapi/serializers/__init__.py +20 -25
  87. package/autorest/multiapi/serializers/import_serializer.py +47 -17
  88. package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
  89. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  90. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  91. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  92. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  93. package/autorest/multiapi/utils.py +3 -3
  94. package/autorest/postprocess/__init__.py +202 -0
  95. package/autorest/postprocess/get_all.py +19 -0
  96. package/autorest/postprocess/venvtools.py +73 -0
  97. package/autorest/preprocess/__init__.py +210 -0
  98. package/autorest/preprocess/helpers.py +54 -0
  99. package/autorest/{namer → preprocess}/python_mappings.py +25 -32
  100. package/package.json +3 -3
  101. package/run-python3.js +2 -3
  102. package/venvtools.py +1 -1
  103. package/autorest/codegen/models/constant_schema.py +0 -101
  104. package/autorest/codegen/models/credential_model.py +0 -47
  105. package/autorest/codegen/models/credential_schema.py +0 -91
  106. package/autorest/codegen/models/credential_schema_policy.py +0 -77
  107. package/autorest/codegen/models/dictionary_schema.py +0 -103
  108. package/autorest/codegen/models/enum_schema.py +0 -215
  109. package/autorest/codegen/models/list_schema.py +0 -123
  110. package/autorest/codegen/models/object_schema.py +0 -253
  111. package/autorest/codegen/models/primitive_schemas.py +0 -466
  112. package/autorest/codegen/models/request_builder_parameter_list.py +0 -280
  113. package/autorest/codegen/models/rest.py +0 -42
  114. package/autorest/codegen/models/schema_request.py +0 -45
  115. package/autorest/codegen/models/schema_response.py +0 -136
  116. package/autorest/codegen/serializers/rest_serializer.py +0 -57
  117. package/autorest/namer/__init__.py +0 -25
  118. package/autorest/namer/name_converter.py +0 -412
@@ -3,41 +3,92 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from collections.abc import MutableSequence
7
- from copy import copy
8
6
  import logging
9
- from typing import cast, List, Callable, Optional, TypeVar, Dict, TYPE_CHECKING
7
+ from typing import (
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ List,
12
+ Optional,
13
+ TYPE_CHECKING,
14
+ Union,
15
+ Generic,
16
+ TypeVar,
17
+ cast,
18
+ )
19
+ from abc import abstractmethod
20
+ from collections.abc import MutableSequence
21
+ from enum import Enum
22
+
23
+ from .request_builder_parameter import (
24
+ RequestBuilderBodyParameter,
25
+ RequestBuilderMultipartBodyParameter,
26
+ RequestBuilderParameter,
27
+ get_request_body_parameter,
28
+ )
29
+ from .parameter import (
30
+ MultipartBodyParameter,
31
+ ParameterLocation,
32
+ BodyParameter,
33
+ Parameter,
34
+ ParameterMethodLocation,
35
+ ClientParameter,
36
+ ConfigParameter,
37
+ get_body_parameter,
38
+ )
39
+
40
+ ParameterType = TypeVar(
41
+ "ParameterType", bound=Union[Parameter, RequestBuilderParameter]
42
+ )
43
+ BodyParameterType = TypeVar(
44
+ "BodyParameterType", bound=Union[BodyParameter, RequestBuilderBodyParameter]
45
+ )
46
+ RequestBuilderBodyParameterType = Union[
47
+ RequestBuilderBodyParameter, RequestBuilderMultipartBodyParameter
48
+ ]
10
49
 
11
- from .parameter import Parameter, ParameterLocation
12
- from .base_schema import BaseSchema
13
- from .dictionary_schema import DictionarySchema
14
- from .primitive_schemas import AnySchema, StringSchema
15
- from .utils import JSON_REGEXP
16
50
 
17
51
  if TYPE_CHECKING:
18
- from .schema_request import SchemaRequest
52
+ from .code_model import CodeModel
53
+
54
+
55
+ class ParameterImplementation(Enum):
56
+ METHOD = "method"
57
+ CLIENT = "client"
19
58
 
20
- T = TypeVar('T')
21
- OrderedSet = Dict[T, None]
22
59
 
23
60
  _LOGGER = logging.getLogger(__name__)
24
61
 
25
- def _method_signature_helper(positional: List[str], keyword_only: Optional[List[str]], kwarg_params: List[str]):
62
+
63
+ def method_signature_helper(
64
+ positional: List[str], keyword_only: Optional[List[str]], kwarg_params: List[str]
65
+ ):
26
66
  keyword_only = keyword_only or []
27
67
  return positional + keyword_only + kwarg_params
28
68
 
29
69
 
30
- class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
70
+ def _sort(params):
71
+ return sorted(
72
+ params, key=lambda x: not (x.client_default_value or x.optional), reverse=True
73
+ )
74
+
75
+
76
+ class _ParameterListBase(
77
+ MutableSequence, Generic[ParameterType, BodyParameterType]
78
+ ): # pylint: disable=too-many-public-methods
79
+ """Base class for all of our different ParameterList classes"""
80
+
31
81
  def __init__(
32
82
  self,
33
- code_model,
34
- parameters: Optional[List[Parameter]] = None,
35
- schema_requests: Optional[List["SchemaRequest"]] = None,
83
+ yaml_data: Dict[str, Any],
84
+ code_model: "CodeModel",
85
+ parameters: List[ParameterType],
86
+ body_parameter: Optional[BodyParameterType] = None,
36
87
  ) -> None:
88
+ self.yaml_data = yaml_data
37
89
  self.code_model = code_model
38
- self.schema_requests = schema_requests or []
39
90
  self.parameters = parameters or []
40
- self._json_body: Optional[BaseSchema] = None
91
+ self._body_parameter = body_parameter
41
92
 
42
93
  # MutableSequence
43
94
 
@@ -55,431 +106,396 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
55
106
  def __delitem__(self, index):
56
107
  del self.parameters[index]
57
108
 
58
- def insert(self, index: int, value: Parameter) -> None:
109
+ def insert(self, index: int, value: ParameterType) -> None:
59
110
  self.parameters.insert(index, value)
60
111
 
61
112
  # Parameter helpers
62
113
 
63
- def has_any_location(self, location: ParameterLocation) -> bool:
64
- return bool([parameter for parameter in self.parameters if parameter.location == location])
65
-
66
- def get_from_predicate(self, predicate: Callable[[Parameter], bool]) -> List[Parameter]:
67
- return [parameter for parameter in self.parameters if predicate(parameter)]
68
-
69
- def get_from_location(self, location: ParameterLocation) -> List[Parameter]:
70
- return self.get_from_predicate(lambda parameter: parameter.location == location)
71
-
72
- @property
73
- def content_types(self) -> List[str]:
74
- ordered_set = {
75
- m: None
76
- for request in self.schema_requests
77
- for m in request.content_types
78
- }
79
- return list(ordered_set.keys())
114
+ @staticmethod
115
+ @abstractmethod
116
+ def parameter_creator() -> Callable[[Dict[str, Any], "CodeModel"], ParameterType]:
117
+ """Callable for creating parameters"""
118
+ ...
80
119
 
81
- @property
82
- def default_content_type(self) -> str:
83
- json_content_types = [c for c in self.content_types if JSON_REGEXP.match(c)]
84
- if json_content_types:
85
- if "application/json" in json_content_types:
86
- return "application/json"
87
- return json_content_types[0]
88
-
89
- xml_content_types = [c for c in self.content_types if "xml" in c]
90
- if xml_content_types:
91
- if "application/xml" in xml_content_types:
92
- return "application/xml"
93
- return xml_content_types[0]
94
- return self.content_types[0]
120
+ @staticmethod
121
+ @abstractmethod
122
+ def body_parameter_creator() -> Callable[
123
+ [Dict[str, Any], "CodeModel"], BodyParameterType
124
+ ]:
125
+ """Callable for creating body parameters"""
126
+ ...
95
127
 
96
128
  @property
97
- def json_body(self) -> BaseSchema:
98
- if not self._json_body:
99
- self._json_body = self.body[0].schema
100
- return self._json_body
129
+ def grouped(self) -> List[Union[ParameterType, BodyParameterType]]:
130
+ """All parameters that are inside a parameter group"""
131
+ params: List[Union[ParameterType, BodyParameterType]] = [
132
+ p for p in self.parameters if p.grouped_by
133
+ ]
134
+ if self.has_body and self.body_parameter.grouped_by:
135
+ params.append(self.body_parameter)
136
+ return params
101
137
 
102
138
  @property
103
139
  def has_body(self) -> bool:
104
- return self.has_any_location(ParameterLocation.Body)
140
+ """Whether there is a body parameter in the parameter list"""
141
+ return bool(self._body_parameter)
105
142
 
106
143
  @property
107
- def body(self) -> List[Parameter]:
108
- if not self.has_body:
109
- raise ValueError(f"Can't get body parameter")
110
- # Should we check if there is two body? Modeler role right?
111
- body_params = self.get_from_location(ParameterLocation.Body)
112
- return body_params
113
-
114
- @staticmethod
115
- def _wanted_path_parameter(parameter: Parameter):
116
- # TODO add 'and parameter.location == "Method"' as requirement to this check once
117
- # I can use send_request on operations.
118
- # Don't want to duplicate code from send_request.
119
- return parameter.location == ParameterLocation.Uri and parameter.rest_api_name != "$host"
120
-
121
- @property
122
- def implementation(self) -> str:
123
- return "Method"
124
-
125
- @property
126
- def path(self) -> List[Parameter]:
144
+ def path(self) -> List[ParameterType]:
145
+ """All path parameters"""
127
146
  return [
128
- parameter
129
- for parameter in self.parameters
130
- if self._wanted_path_parameter(parameter)
147
+ p
148
+ for p in self.parameters
149
+ if p.location in (ParameterLocation.PATH, ParameterLocation.ENDPOINT_PATH)
131
150
  ]
132
151
 
133
152
  @property
134
- def query(self) -> List[Parameter]:
135
- return self.get_from_location(ParameterLocation.Query)
136
-
137
- @property
138
- def headers(self) -> List[Parameter]:
139
- headers = self.get_from_location(ParameterLocation.Header)
140
- if not headers:
141
- return headers
142
- return list({
143
- header.serialized_name: header
144
- for header in headers
145
- }.values())
153
+ def query(self) -> List[ParameterType]:
154
+ """All query parameters"""
155
+ return [p for p in self.parameters if p.location == ParameterLocation.QUERY]
146
156
 
147
157
  @property
148
- def grouped(self) -> List[Parameter]:
149
- return self.get_from_predicate(lambda parameter: cast(bool, parameter.grouped_by))
158
+ def headers(self) -> List[ParameterType]:
159
+ """All header parameters"""
160
+ return [p for p in self.parameters if p.location == ParameterLocation.HEADER]
150
161
 
151
162
  @property
152
- def groupers(self) -> List[Parameter]:
153
- groupers: List[Parameter] = []
154
- for parameter in self.parameters:
155
- if any([
156
- p for p in self.grouped
157
- if p.grouped_by and id(p.grouped_by.yaml_data) == id(parameter.yaml_data)
158
- ]):
159
- groupers.append(parameter)
160
- return groupers
163
+ def constant(self) -> List[Union[ParameterType, BodyParameterType]]:
164
+ """All constant parameters"""
165
+ return [p for p in self.parameters if p.constant]
161
166
 
162
167
  @property
163
- def constant(self) -> List[Parameter]:
164
- """Return the constants of this parameter list.
165
-
166
- This excludes the constant from flatening on purpose, since technically they are not
167
- constant from this set of parameters, they are constants on the models and hence they do
168
- not have impact on any generation at this level
169
- """
170
- return self.get_from_predicate(
171
- lambda parameter: parameter.constant
168
+ def positional(self) -> List[Union[ParameterType, BodyParameterType]]:
169
+ """All positional parameters"""
170
+ return _sort(
171
+ [
172
+ p
173
+ for p in self.unsorted_method_params
174
+ if p.method_location == ParameterMethodLocation.POSITIONAL
175
+ ]
172
176
  )
173
177
 
174
178
  @property
175
- def multipart(self) -> List[Parameter]:
176
- return self.get_from_predicate(
177
- lambda parameter: parameter.is_multipart and not parameter.is_body_kwarg
179
+ def keyword_only(self) -> List[Union[ParameterType, BodyParameterType]]:
180
+ """All keyword only parameters"""
181
+ return _sort(
182
+ [
183
+ p
184
+ for p in self.unsorted_method_params
185
+ if p.method_location == ParameterMethodLocation.KEYWORD_ONLY
186
+ ]
178
187
  )
179
188
 
180
189
  @property
181
- def data_inputs(self) -> List[Parameter]:
182
- return self.get_from_predicate(
183
- lambda parameter: parameter.is_data_input and not parameter.is_body_kwarg
190
+ def kwarg(self) -> List[Union[ParameterType, BodyParameterType]]:
191
+ """All kwargs"""
192
+ return _sort(
193
+ [
194
+ p
195
+ for p in self.unsorted_method_params
196
+ if p.method_location == ParameterMethodLocation.KWARG
197
+ ]
184
198
  )
185
199
 
186
- def _filter_out_multiple_content_type(self, kwarg_params):
187
- """We don't want multiple content type kwargs in the method signature"""
188
- content_type_params = [k for k in kwarg_params if k.rest_api_name == "Content-Type"]
189
- if len(content_type_params) > 1:
190
- # we don't want multiple content type params in the method, just one
191
- # we'll pick the one with the default content type
192
- kwarg_params = [
193
- k for k in kwarg_params
194
- if not (
195
- k.rest_api_name == "Content-Type"
196
- and k.default_value_declaration != f'"{self.default_content_type}"'
197
- )
198
- ]
199
- return kwarg_params
200
+ @property
201
+ def body_parameter(self) -> BodyParameterType:
202
+ """The body parameter of the parameter list. Will only ever be at most one."""
203
+ if not self._body_parameter:
204
+ raise ValueError("There is no body parameter")
205
+ return self._body_parameter
200
206
 
201
207
  @property
202
- def method(self) -> List[Parameter]:
203
- """The list of parameter used in method signature.
204
- """
205
- # Client level should not be on Method, etc.
206
- parameters_of_this_implementation = self.get_from_predicate(
207
- lambda parameter: parameter.implementation == self.implementation
208
- )
209
- positional = [p for p in parameters_of_this_implementation if p.is_positional]
210
- keyword_only = self._filter_out_multiple_content_type(
211
- [p for p in parameters_of_this_implementation if p.is_keyword_only]
212
- )
213
- kwargs = self._filter_out_multiple_content_type(
214
- [p for p in parameters_of_this_implementation if p.is_kwarg]
215
- )
216
- def _sort(params):
217
- return sorted(params, key=lambda x: not x.default_value and x.required, reverse=True)
218
- signature_parameters = (
219
- _sort(positional) + _sort(keyword_only) + _sort(kwargs)
220
- )
221
- return signature_parameters
208
+ @abstractmethod
209
+ def implementation(self) -> str:
210
+ """Whether this is a client or a method parameter"""
211
+ ...
222
212
 
213
+ @property
214
+ def unsorted_method_params(self) -> List[Union[ParameterType, BodyParameterType]]:
215
+ """Method params before sorting"""
216
+ method_params: List[Union[ParameterType, BodyParameterType]] = [
217
+ p
218
+ for p in self.parameters
219
+ if p.in_method_signature and p.implementation == self.implementation
220
+ ]
221
+ if self._body_parameter:
222
+ if self._body_parameter.in_method_signature:
223
+ method_params.append(self._body_parameter)
224
+ try:
225
+ # i am a multipart body parameter
226
+ # Only legacy generates operations with me, so I will follow the legacy rules
227
+ # I will splat out my entries as individual entries
228
+ method_params.extend(self._body_parameter.entries) # type: ignore
229
+ except AttributeError:
230
+ pass
231
+ return method_params
223
232
 
224
- def method_signature(self, is_python3_file: bool) -> List[str]:
225
- return _method_signature_helper(
226
- positional=self.method_signature_positional(is_python3_file),
227
- keyword_only=self.method_signature_keyword_only(is_python3_file),
228
- kwarg_params=self.method_signature_kwargs(is_python3_file)
233
+ @property
234
+ def method(self) -> List[Union[ParameterType, BodyParameterType]]:
235
+ """Sorted method params. First positional, then keyword only, then kwarg"""
236
+ return self.positional + self.keyword_only + self.kwarg
237
+
238
+ def method_signature(self, is_python3_file: bool, async_mode: bool) -> List[str]:
239
+ """Method signature for this parameter list."""
240
+ return method_signature_helper(
241
+ positional=self.method_signature_positional(is_python3_file, async_mode),
242
+ keyword_only=self.method_signature_keyword_only(
243
+ is_python3_file, async_mode
244
+ ),
245
+ kwarg_params=self.method_signature_kwargs(is_python3_file),
229
246
  )
230
247
 
231
- def method_signature_positional(self, is_python3_file: bool) -> List[str]:
232
- return [parameter.method_signature(is_python3_file) for parameter in self.positional]
248
+ def method_signature_positional(
249
+ self, is_python3_file: bool, async_mode: bool
250
+ ) -> List[str]:
251
+ """Signature for positional parameters"""
252
+ return [
253
+ parameter.method_signature(is_python3_file, async_mode)
254
+ for parameter in self.positional
255
+ ]
233
256
 
234
- def method_signature_keyword_only(self, is_python3_file: bool) -> List[str]:
257
+ def method_signature_keyword_only(
258
+ self, is_python3_file: bool, async_mode: bool
259
+ ) -> List[str]:
260
+ """Signature for keyword only parameters"""
235
261
  if not (self.keyword_only and is_python3_file):
236
262
  return []
237
- return ["*,"] + [parameter.method_signature(is_python3_file) for parameter in self.keyword_only]
263
+ return ["*,"] + [
264
+ parameter.method_signature(is_python3_file, async_mode)
265
+ for parameter in self.keyword_only
266
+ ]
238
267
 
239
268
  @staticmethod
240
269
  def method_signature_kwargs(is_python3_file: bool) -> List[str]:
270
+ """Signature for kwargs"""
241
271
  return ["**kwargs: Any"] if is_python3_file else ["**kwargs # type: Any"]
242
272
 
243
- @property
244
- def positional(self) -> List[Parameter]:
245
- return [p for p in self.method if p.is_positional]
246
-
247
- @property
248
- def keyword_only(self) -> List[Parameter]:
249
- return [p for p in self.method if p.is_keyword_only]
250
-
251
- @property
252
- def kwargs(self) -> List[Parameter]:
253
- return [p for p in self.method if p.is_kwarg]
254
-
255
- def kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
256
- kwargs_to_pop = self.kwargs
273
+ def kwargs_to_pop(
274
+ self, is_python3_file: bool
275
+ ) -> List[Union[ParameterType, BodyParameterType]]:
276
+ """Method kwargs we want to pop"""
277
+ # don't want to pop bodies unless it's a constant
278
+ kwargs_to_pop = self.kwarg
257
279
  if not is_python3_file:
258
280
  kwargs_to_pop += self.keyword_only
259
- return kwargs_to_pop
281
+ return [
282
+ k
283
+ for k in kwargs_to_pop
284
+ if k.location != ParameterLocation.BODY or k.constant
285
+ ]
260
286
 
261
287
  @property
262
288
  def call(self) -> List[str]:
289
+ """How to pass in parameters to call the operation"""
263
290
  retval = [
264
- p.serialized_name for p in self.positional
291
+ p.client_name
292
+ for p in self.method
293
+ if p.method_location == ParameterMethodLocation.POSITIONAL
265
294
  ]
266
- retval.extend([
267
- f"{p.serialized_name}={p.serialized_name}"
268
- for p in self.keyword_only
269
- ])
295
+ retval.extend(
296
+ [
297
+ f"{p.client_name}={p.client_name}"
298
+ for p in self.method
299
+ if p.method_location == ParameterMethodLocation.KEYWORD_ONLY
300
+ ]
301
+ )
270
302
  retval.append("**kwargs")
271
303
  return retval
272
304
 
273
- @property
274
- def is_flattened(self) -> bool:
275
- return cast(bool, self.get_from_predicate(lambda parameter: parameter.flattened))
276
-
277
- def _create_files_or_data_param(
278
- params: List[Parameter], serialized_name: str, description: str
279
- ) -> Parameter:
280
- params[0].need_import = False
281
- param = copy(params[0])
282
- param.serialized_name = serialized_name
283
- param.schema = DictionarySchema(
284
- namespace="",
285
- yaml_data={},
286
- element_type=AnySchema(namespace="", yaml_data={}),
287
- )
288
- param.description = description
289
- return param
305
+ @classmethod
306
+ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):
307
+ parameters = [
308
+ cls.parameter_creator()(parameter, code_model)
309
+ for parameter in yaml_data["parameters"]
310
+ ]
311
+ body_parameter = None
312
+ if yaml_data.get("bodyParameter"):
313
+ body_parameter = cls.body_parameter_creator()(
314
+ yaml_data["bodyParameter"], code_model
315
+ )
316
+ return cls(
317
+ yaml_data,
318
+ code_model,
319
+ parameters=parameters,
320
+ body_parameter=body_parameter,
321
+ )
322
+
323
+
324
+ class _ParameterList(
325
+ _ParameterListBase[ # pylint: disable=unsubscriptable-object
326
+ Parameter, Union[MultipartBodyParameter, BodyParameter]
327
+ ]
328
+ ):
329
+ """Base Parameter class for the two operation ParameterLists"""
330
+
331
+ @staticmethod
332
+ def parameter_creator() -> Callable[[Dict[str, Any], "CodeModel"], Parameter]:
333
+ return Parameter.from_yaml
290
334
 
291
- class ParameterOnlyPathAndBodyPositionalList(ParameterList):
292
- # use this to change the files and data parameter in the method
335
+ @staticmethod
336
+ def body_parameter_creator() -> Callable[
337
+ [Dict[str, Any], "CodeModel"], Union[MultipartBodyParameter, BodyParameter]
338
+ ]:
339
+ return get_body_parameter
293
340
 
294
341
  @property
295
- def method(self) -> List[Parameter]:
296
- method_params = super().method
297
- files_params = [p for p in method_params if p.is_multipart]
298
- data_params = [p for p in method_params if p.is_data_input]
299
- if not (files_params or data_params):
300
- return method_params
301
-
302
- # update files param
303
- file_and_data_params = []
304
- if files_params:
305
- files_param = _create_files_or_data_param(
306
- files_params,
307
- serialized_name="files",
308
- description="Multipart input for files. See the template in our example to find the input shape."
309
- )
310
- file_and_data_params.append(files_param)
311
- if data_params:
312
- data_param = _create_files_or_data_param(
313
- data_params,
314
- serialized_name="data",
315
- description="Form-encoded input for data. See the template in our example to find the input shape."
316
- )
317
- file_and_data_params.append(data_param)
318
- method_params = [p for p in method_params if not p.is_multipart and not p.is_data_input]
319
- positional = [p for p in method_params if p.is_positional]
320
- keyword_only = self._filter_out_multiple_content_type(
321
- [p for p in method_params if p.is_keyword_only]
322
- )
323
- kwargs = self._filter_out_multiple_content_type(
324
- [p for p in method_params if p.is_kwarg]
325
- )
326
- return positional + file_and_data_params + keyword_only + kwargs
342
+ def implementation(self) -> str:
343
+ return "Method"
344
+
345
+ @property
346
+ def path(self) -> List[Parameter]:
347
+ return [
348
+ k for k in super().path if k.location == ParameterLocation.ENDPOINT_PATH
349
+ ]
327
350
 
328
- def get_parameter_list(code_model):
329
- if code_model.options["only_path_and_body_params_positional"]:
330
- return ParameterOnlyPathAndBodyPositionalList
331
- return ParameterList
332
351
 
333
- class GlobalParameterList(ParameterList):
352
+ class ParameterList(_ParameterList):
353
+ """ParameterList is the parameter list for Operation classes"""
354
+
355
+ ...
356
+
357
+
358
+ class _RequestBuilderParameterList(
359
+ _ParameterListBase[ # pylint: disable=unsubscriptable-object
360
+ RequestBuilderParameter, RequestBuilderBodyParameterType
361
+ ]
362
+ ):
363
+ """_RequestBuilderParameterList is base parameter list for RequestBuilder classes"""
364
+
365
+ @staticmethod
366
+ def parameter_creator() -> Callable[
367
+ [Dict[str, Any], "CodeModel"], RequestBuilderParameter
368
+ ]:
369
+ return RequestBuilderParameter.from_yaml
370
+
371
+ @staticmethod
372
+ def body_parameter_creator() -> Callable[
373
+ [Dict[str, Any], "CodeModel"], RequestBuilderBodyParameterType
374
+ ]:
375
+ return get_request_body_parameter
334
376
 
335
377
  @property
336
378
  def implementation(self) -> str:
337
- return "Client"
379
+ return "Method"
338
380
 
339
381
  @property
340
- def method(self) -> List[Parameter]:
341
- """The list of parameter used in method signature.
342
- """
343
- # Client level should not be on Method, etc.
344
- positional = [p for p in self.parameters if p.is_positional]
345
- keyword_only = self._filter_out_multiple_content_type(
346
- [p for p in self.parameters if p.is_keyword_only]
347
- )
348
- kwargs = self._filter_out_multiple_content_type(
349
- [p for p in self.parameters if p.is_kwarg]
350
- )
351
- def _sort(params):
352
- return sorted(params, key=lambda x: not x.default_value and x.required, reverse=True)
353
- signature_parameters = (
354
- _sort(positional) + _sort(keyword_only) + _sort(kwargs)
382
+ def unsorted_method_params(
383
+ self,
384
+ ) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameterType]]:
385
+ # don't have access to client params in request builder
386
+ retval = [
387
+ p
388
+ for p in super().unsorted_method_params
389
+ if not (
390
+ p.location == ParameterLocation.BODY
391
+ and cast(RequestBuilderBodyParameterType, p).is_partial_body
392
+ )
393
+ ]
394
+ retval.extend(
395
+ [
396
+ p
397
+ for p in self.parameters
398
+ if p.implementation == "Client" and p.in_method_signature
399
+ ]
355
400
  )
356
- return signature_parameters
401
+ return retval
357
402
 
358
403
  @property
359
- def code_model(self):
360
- try:
361
- return self._code_model
362
- except AttributeError:
363
- raise ValueError("You need to first set the code model")
404
+ def path(self) -> List[RequestBuilderParameter]:
405
+ return [
406
+ p for p in super().path if p.location != ParameterLocation.ENDPOINT_PATH
407
+ ]
364
408
 
365
- @code_model.setter
366
- def code_model(self, val):
367
- self._code_model = val
368
409
 
369
- @property
370
- def host_variable_name(self) -> str:
371
- return (
372
- "endpoint" if self.code_model.options["version_tolerant"]
373
- or self.code_model.options["low_level_client"]
374
- else "base_url"
375
- )
410
+ class RequestBuilderParameterList(_RequestBuilderParameterList):
411
+ """Parameter list for Request Builder"""
412
+
413
+ ...
414
+
415
+
416
+ class OverloadedRequestBuilderParameterList(_RequestBuilderParameterList):
417
+ """Parameter list for OverloadedRequestBuilder"""
418
+
419
+ def method_signature_keyword_only(
420
+ self, is_python3_file: bool, async_mode: bool
421
+ ) -> List[str]:
422
+ """Signature for keyword only parameters"""
423
+ if not (self.keyword_only and is_python3_file):
424
+ return []
425
+ return ["*,"] + [
426
+ parameter.method_signature(is_python3_file, async_mode)
427
+ for parameter in self.keyword_only
428
+ if parameter.location != ParameterLocation.BODY
429
+ ]
430
+
431
+
432
+ class _ClientGlobalParameterList(
433
+ # pylint: disable=unsubscriptable-object
434
+ _ParameterListBase[ParameterType, BodyParameter]
435
+ ):
436
+ """Base parameter list for client and config classes"""
376
437
 
377
438
  @staticmethod
378
- def _wanted_path_parameter(parameter: Parameter) -> bool:
379
- return (
380
- parameter.location == ParameterLocation.Uri and
381
- parameter.implementation == "Client" and
382
- parameter.rest_api_name != "$host"
383
- )
439
+ def body_parameter_creator() -> Callable[
440
+ [Dict[str, Any], "CodeModel"], BodyParameter
441
+ ]:
442
+ return BodyParameter.from_yaml
384
443
 
385
- def add_host(self, host_value: Optional[str]) -> None:
386
- # only adds if we don't have a parameterized host
387
- host_param = Parameter(
388
- self.code_model,
389
- yaml_data={},
390
- schema=StringSchema(namespace="", yaml_data={"type": "str"}),
391
- rest_api_name=self.host_variable_name,
392
- serialized_name=self.host_variable_name,
393
- description=f"Service URL.",
394
- implementation="Client",
395
- required=True,
396
- location=ParameterLocation.Other,
397
- skip_url_encoding=False,
398
- constraints=[],
399
- client_default_value=host_value,
400
- keyword_only=self.code_model.options["version_tolerant"] or self.code_model.options["low_level_client"],
401
- )
402
- self.parameters.append(host_param)
403
-
404
- def add_credential_global_parameter(self) -> None:
405
- credential_parameter = Parameter(
406
- self.code_model,
407
- yaml_data={},
408
- schema=self.code_model.credential_model.credential_schema_policy.credential,
409
- serialized_name="credential",
410
- rest_api_name="credential",
411
- implementation="Client",
412
- description="Credential needed for the client to connect to Azure.",
413
- required=True,
414
- location=ParameterLocation.Other,
415
- skip_url_encoding=True,
416
- constraints=[],
417
- )
418
- if self.code_model.options["version_tolerant"] or self.code_model.options["low_level_client"]:
419
- self.parameters.append(credential_parameter)
420
- else:
421
- self.parameters.insert(0, credential_parameter)
444
+ @property
445
+ def implementation(self) -> str:
446
+ return "Client"
422
447
 
423
448
  @property
424
- def host(self) -> Optional[Parameter]:
449
+ def credential(self) -> Optional[ParameterType]:
425
450
  try:
426
- return next(p for p in self.parameters if p.serialized_name == self.host_variable_name)
451
+ return next(p for p in self.parameters if p.client_name == "credential")
427
452
  except StopIteration:
428
453
  return None
429
454
 
430
455
  @property
431
- def host_value(self) -> Optional[str]:
432
- if self.code_model.service_client.has_parameterized_host:
433
- return None
434
- return next(
435
- p for p in self.parameters
436
- if p.serialized_name == self.host_variable_name
437
- ).default_value_declaration
456
+ def path(self) -> List[ParameterType]:
457
+ return [
458
+ p for p in super().path if p.location == ParameterLocation.ENDPOINT_PATH
459
+ ]
460
+
461
+
462
+ class ClientGlobalParameterList(_ClientGlobalParameterList[ClientParameter]):
463
+ """Parameter list for Client class"""
464
+
465
+ @staticmethod
466
+ def parameter_creator() -> Callable[[Dict[str, Any], "CodeModel"], ClientParameter]:
467
+ return ClientParameter.from_yaml
438
468
 
439
469
  @property
440
- def client_method(self) -> List[Parameter]:
441
- return self.method
470
+ def path(self) -> List[ClientParameter]:
471
+ return [p for p in super().path if not p.is_host]
442
472
 
443
- def _param_is_in_config_method(self, serialized_name):
444
- if self.code_model.service_client.has_parameterized_host:
445
- return True
446
- return serialized_name != self.host_variable_name
473
+ @property
474
+ def host(self) -> Optional[ClientParameter]:
475
+ """Get the host parameter"""
476
+ try:
477
+ return next(p for p in self.parameters if p.is_host)
478
+ except StopIteration:
479
+ return None
447
480
 
448
- def kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
481
+ def kwargs_to_pop(
482
+ self, is_python3_file: bool
483
+ ) -> List[Union[ClientParameter, BodyParameter]]:
484
+ """We only want to pass base url path parameters in the client"""
449
485
  return [
450
- k for k in super().kwargs_to_pop(is_python3_file)
451
- if not self._param_is_in_config_method(k.serialized_name)
486
+ k
487
+ for k in super().kwargs_to_pop(is_python3_file=is_python3_file)
488
+ if k.location == ParameterLocation.ENDPOINT_PATH
452
489
  ]
453
490
 
454
- def config_kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
455
- current_kwargs_to_pop = super().kwargs_to_pop(is_python3_file)
456
- return [k for k in current_kwargs_to_pop if self._param_is_in_config_method(k.serialized_name)]
457
491
 
458
- @property
459
- def config_method(self) -> List[Parameter]:
460
- return [p for p in self.method if self._param_is_in_config_method(p.serialized_name)]
461
-
462
- def client_method_signature(self, is_python3_file: bool) -> List[str]:
463
- return self.method_signature(is_python3_file)
492
+ class ConfigGlobalParameterList(_ClientGlobalParameterList[ConfigParameter]):
493
+ """Parameter list for config"""
464
494
 
465
- def config_method_signature(self, is_python3_file: bool) -> List[str]:
495
+ @staticmethod
496
+ def parameter_creator() -> Callable[[Dict[str, Any], "CodeModel"], ConfigParameter]:
497
+ return ConfigParameter.from_yaml
466
498
 
467
- positional = [
468
- p.method_signature(is_python3_file)
469
- for p in self.positional
470
- if self._param_is_in_config_method(p.serialized_name)
471
- ]
472
- keyword_only_params = [p for p in self.keyword_only if self._param_is_in_config_method(p.serialized_name)]
473
- keyword_only_method_signature = []
474
- if is_python3_file:
475
- keyword_only_method_signature = (
476
- ["*,"] +
477
- [
478
- p.method_signature(is_python3_file) for p in keyword_only_params
479
- ]
480
- ) if keyword_only_params else []
481
- return _method_signature_helper(
482
- positional=positional,
483
- keyword_only=keyword_only_method_signature,
484
- kwarg_params=self.method_signature_kwargs(is_python3_file)
485
- )
499
+ @property
500
+ def implementation(self) -> str:
501
+ return "Client"