@autorest/python 6.12.2 → 6.12.4
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.
- package/autorest/_utils.py +1 -1
- package/autorest/codegen/models/__init__.py +0 -2
- package/autorest/codegen/models/base.py +4 -0
- package/autorest/codegen/models/combined_type.py +4 -0
- package/autorest/codegen/models/model_type.py +13 -0
- package/autorest/codegen/models/operation.py +1 -23
- package/autorest/codegen/models/parameter.py +26 -51
- package/autorest/codegen/models/parameter_list.py +9 -19
- package/autorest/codegen/models/property.py +15 -2
- package/autorest/codegen/models/request_builder_parameter.py +1 -37
- package/autorest/codegen/serializers/builder_serializer.py +45 -48
- package/autorest/codegen/serializers/general_serializer.py +20 -8
- package/autorest/codegen/serializers/model_serializer.py +4 -1
- package/autorest/codegen/serializers/parameter_serializer.py +2 -2
- package/autorest/codegen/templates/model_base.py.jinja2 +21 -7
- package/autorest/codegen/templates/packaging_templates/setup.py.jinja2 +1 -1
- package/autorest/codegen/templates/vendor.py.jinja2 +35 -31
- package/package.json +1 -1
- package/requirements.txt +1 -1
- package/run_cadl.py +1 -1
package/autorest/_utils.py
CHANGED
|
@@ -43,7 +43,6 @@ from .parameter import (
|
|
|
43
43
|
ParameterLocation,
|
|
44
44
|
BodyParameter,
|
|
45
45
|
ParameterDelimeter,
|
|
46
|
-
MultipartBodyParameter,
|
|
47
46
|
ClientParameter,
|
|
48
47
|
ConfigParameter,
|
|
49
48
|
)
|
|
@@ -115,7 +114,6 @@ __all__ = [
|
|
|
115
114
|
"BodyParameter",
|
|
116
115
|
"RequestBuilderBodyParameter",
|
|
117
116
|
"ParameterDelimeter",
|
|
118
|
-
"MultipartBodyParameter",
|
|
119
117
|
"CredentialType",
|
|
120
118
|
"ClientParameter",
|
|
121
119
|
"ConfigParameter",
|
|
@@ -79,6 +79,10 @@ class CombinedType(BaseType):
|
|
|
79
79
|
pattern = re.compile(r"Union\[.*\]")
|
|
80
80
|
return f'Union[{", ".join(map(lambda x: x[6: -1] if pattern.match(x) else x, inside_types))}]'
|
|
81
81
|
|
|
82
|
+
@property
|
|
83
|
+
def is_form_data(self) -> bool:
|
|
84
|
+
return any(t.is_form_data for t in self.types)
|
|
85
|
+
|
|
82
86
|
def get_json_template_representation(
|
|
83
87
|
self,
|
|
84
88
|
*,
|
|
@@ -76,6 +76,10 @@ class ModelType( # pylint: disable=abstract-method
|
|
|
76
76
|
self.snake_case_name: str = self.yaml_data["snakeCaseName"]
|
|
77
77
|
self.page_result_model: bool = self.yaml_data.get("pageResultModel", False)
|
|
78
78
|
|
|
79
|
+
@property
|
|
80
|
+
def is_form_data(self) -> bool:
|
|
81
|
+
return any(p.is_multipart_file_input for p in self.properties)
|
|
82
|
+
|
|
79
83
|
@property
|
|
80
84
|
def is_xml(self) -> bool:
|
|
81
85
|
return self.yaml_data.get("isXml", False)
|
|
@@ -314,6 +318,15 @@ class GeneratedModelType(ModelType): # pylint: disable=abstract-method
|
|
|
314
318
|
if kwargs.get("model_typing")
|
|
315
319
|
else TypingSection.REGULAR,
|
|
316
320
|
)
|
|
321
|
+
if self.is_form_data:
|
|
322
|
+
file_import.add_submodule_import(
|
|
323
|
+
relative_path,
|
|
324
|
+
"_model_base",
|
|
325
|
+
ImportType.LOCAL,
|
|
326
|
+
typing_section=TypingSection.TYPING
|
|
327
|
+
if kwargs.get("model_typing")
|
|
328
|
+
else TypingSection.REGULAR,
|
|
329
|
+
)
|
|
317
330
|
return file_import
|
|
318
331
|
|
|
319
332
|
|
|
@@ -30,7 +30,6 @@ from .response import (
|
|
|
30
30
|
)
|
|
31
31
|
from .parameter import (
|
|
32
32
|
BodyParameter,
|
|
33
|
-
MultipartBodyParameter,
|
|
34
33
|
Parameter,
|
|
35
34
|
ParameterLocation,
|
|
36
35
|
)
|
|
@@ -290,7 +289,6 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
290
289
|
Parameter,
|
|
291
290
|
RequestBuilderParameter,
|
|
292
291
|
BodyParameter,
|
|
293
|
-
MultipartBodyParameter,
|
|
294
292
|
]
|
|
295
293
|
],
|
|
296
294
|
location: ParameterLocation,
|
|
@@ -575,33 +573,13 @@ class Operation(OperationBase[Response]):
|
|
|
575
573
|
relative_path = "..." if async_mode else ".."
|
|
576
574
|
if self.code_model.options["models_mode"] == "dpg":
|
|
577
575
|
if self.parameters.has_body:
|
|
578
|
-
if not self.
|
|
576
|
+
if not self.has_form_data_body:
|
|
579
577
|
file_import.add_submodule_import(
|
|
580
578
|
f"{relative_path}_model_base",
|
|
581
579
|
"SdkJSONEncoder",
|
|
582
580
|
ImportType.LOCAL,
|
|
583
581
|
)
|
|
584
582
|
file_import.add_import("json", ImportType.STDLIB)
|
|
585
|
-
else:
|
|
586
|
-
file_import.add_submodule_import(
|
|
587
|
-
relative_path, "_model_base", ImportType.LOCAL
|
|
588
|
-
)
|
|
589
|
-
file_import.add_submodule_import("io", "IOBase", ImportType.STDLIB)
|
|
590
|
-
file_import.add_submodule_import(
|
|
591
|
-
f"{relative_path}_vendor",
|
|
592
|
-
"multipart_file",
|
|
593
|
-
ImportType.LOCAL,
|
|
594
|
-
)
|
|
595
|
-
file_import.add_submodule_import(
|
|
596
|
-
f"{relative_path}_vendor",
|
|
597
|
-
"multipart_data",
|
|
598
|
-
ImportType.LOCAL,
|
|
599
|
-
)
|
|
600
|
-
file_import.add_submodule_import(
|
|
601
|
-
f"{relative_path}_vendor",
|
|
602
|
-
"handle_multipart_form_data_model",
|
|
603
|
-
ImportType.LOCAL,
|
|
604
|
-
)
|
|
605
583
|
if self.default_error_deserialization or any(
|
|
606
584
|
r.type for r in self.responses
|
|
607
585
|
):
|
|
@@ -14,7 +14,6 @@ from typing import (
|
|
|
14
14
|
Optional,
|
|
15
15
|
TypeVar,
|
|
16
16
|
Union,
|
|
17
|
-
Generic,
|
|
18
17
|
)
|
|
19
18
|
|
|
20
19
|
from .imports import FileImport, ImportType, TypingSection
|
|
@@ -243,9 +242,18 @@ class _ParameterBase(
|
|
|
243
242
|
class BodyParameter(_ParameterBase):
|
|
244
243
|
"""Body parameter."""
|
|
245
244
|
|
|
245
|
+
@property
|
|
246
|
+
def entries(self) -> List["BodyParameter"]:
|
|
247
|
+
return [
|
|
248
|
+
BodyParameter.from_yaml(e, self.code_model)
|
|
249
|
+
for e in self.yaml_data.get("entries", [])
|
|
250
|
+
]
|
|
251
|
+
|
|
246
252
|
@property
|
|
247
253
|
def is_form_data(self) -> bool:
|
|
248
|
-
|
|
254
|
+
# hacky, but rn in legacy, there is no formdata model type, it's just a dict
|
|
255
|
+
# with all of the entries splatted out
|
|
256
|
+
return self.type.is_form_data or bool(self.entries)
|
|
249
257
|
|
|
250
258
|
@property
|
|
251
259
|
def is_partial_body(self) -> bool:
|
|
@@ -262,6 +270,10 @@ class BodyParameter(_ParameterBase):
|
|
|
262
270
|
|
|
263
271
|
@property
|
|
264
272
|
def in_method_signature(self) -> bool:
|
|
273
|
+
if self.yaml_data.get("entries"):
|
|
274
|
+
# Right now, only legacy generates with multipart bodies and entries
|
|
275
|
+
# and legacy generates with the multipart body arguments splatted out
|
|
276
|
+
return False
|
|
265
277
|
return not (self.flattened or self.grouped_by)
|
|
266
278
|
|
|
267
279
|
@property
|
|
@@ -278,6 +290,18 @@ class BodyParameter(_ParameterBase):
|
|
|
278
290
|
return self.type.target_model_subtype((JSONModelType,)) is not None
|
|
279
291
|
return isinstance(self.type, JSONModelType)
|
|
280
292
|
|
|
293
|
+
def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
|
|
294
|
+
file_import = super().imports(async_mode, **kwargs)
|
|
295
|
+
if self.is_form_data:
|
|
296
|
+
relative_path = "..." if async_mode else ".."
|
|
297
|
+
file_import.add_submodule_import(
|
|
298
|
+
f"{relative_path}_vendor",
|
|
299
|
+
"prepare_multipart_form_data",
|
|
300
|
+
ImportType.LOCAL,
|
|
301
|
+
)
|
|
302
|
+
file_import.add_submodule_import("typing", "List", ImportType.STDLIB)
|
|
303
|
+
return file_import
|
|
304
|
+
|
|
281
305
|
@classmethod
|
|
282
306
|
def from_yaml(
|
|
283
307
|
cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
|
|
@@ -294,46 +318,6 @@ EntryBodyParameterType = TypeVar(
|
|
|
294
318
|
)
|
|
295
319
|
|
|
296
320
|
|
|
297
|
-
class _MultipartBodyParameter(Generic[EntryBodyParameterType], BodyParameter):
|
|
298
|
-
"""Base class for MultipartBodyParameter and RequestBuilderMultipartBodyParameter"""
|
|
299
|
-
|
|
300
|
-
def __init__(
|
|
301
|
-
self,
|
|
302
|
-
yaml_data: Dict[str, Any],
|
|
303
|
-
code_model: "CodeModel",
|
|
304
|
-
type: BaseType,
|
|
305
|
-
entries: List[EntryBodyParameterType],
|
|
306
|
-
) -> None:
|
|
307
|
-
super().__init__(yaml_data, code_model, type)
|
|
308
|
-
self.entries = entries
|
|
309
|
-
|
|
310
|
-
@property
|
|
311
|
-
def in_method_signature(self) -> bool:
|
|
312
|
-
# Right now, only legacy generates with multipart bodies
|
|
313
|
-
# and legacy generates with the multipart body arguments splatted out
|
|
314
|
-
return False
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
class MultipartBodyParameter(
|
|
318
|
-
_MultipartBodyParameter[BodyParameter] # pylint: disable=unsubscriptable-object
|
|
319
|
-
):
|
|
320
|
-
"""Multipart body parameter for Operation. Used for files and data input."""
|
|
321
|
-
|
|
322
|
-
@classmethod
|
|
323
|
-
def from_yaml(
|
|
324
|
-
cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
|
|
325
|
-
) -> "MultipartBodyParameter":
|
|
326
|
-
return cls(
|
|
327
|
-
yaml_data=yaml_data,
|
|
328
|
-
code_model=code_model,
|
|
329
|
-
type=code_model.lookup_type(id(yaml_data["type"])),
|
|
330
|
-
entries=[
|
|
331
|
-
BodyParameter.from_yaml(entry, code_model)
|
|
332
|
-
for entry in yaml_data["entries"]
|
|
333
|
-
],
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
|
|
337
321
|
class Parameter(_ParameterBase):
|
|
338
322
|
"""Basic Parameter class"""
|
|
339
323
|
|
|
@@ -455,12 +439,3 @@ class ConfigParameter(Parameter):
|
|
|
455
439
|
if self.constant:
|
|
456
440
|
return ParameterMethodLocation.KWARG
|
|
457
441
|
return ParameterMethodLocation.POSITIONAL
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
def get_body_parameter(
|
|
461
|
-
yaml_data: Dict[str, Any], code_model: "CodeModel"
|
|
462
|
-
) -> Union[BodyParameter, MultipartBodyParameter]:
|
|
463
|
-
"""Creates a regular body parameter or Multipart body parameter"""
|
|
464
|
-
if yaml_data.get("entries"):
|
|
465
|
-
return MultipartBodyParameter.from_yaml(yaml_data, code_model)
|
|
466
|
-
return BodyParameter.from_yaml(yaml_data, code_model)
|
|
@@ -22,19 +22,15 @@ from enum import Enum
|
|
|
22
22
|
|
|
23
23
|
from .request_builder_parameter import (
|
|
24
24
|
RequestBuilderBodyParameter,
|
|
25
|
-
RequestBuilderMultipartBodyParameter,
|
|
26
25
|
RequestBuilderParameter,
|
|
27
|
-
get_request_body_parameter,
|
|
28
26
|
)
|
|
29
27
|
from .parameter import (
|
|
30
|
-
MultipartBodyParameter,
|
|
31
28
|
ParameterLocation,
|
|
32
29
|
BodyParameter,
|
|
33
30
|
Parameter,
|
|
34
31
|
ParameterMethodLocation,
|
|
35
32
|
ClientParameter,
|
|
36
33
|
ConfigParameter,
|
|
37
|
-
get_body_parameter,
|
|
38
34
|
)
|
|
39
35
|
|
|
40
36
|
ParameterType = TypeVar(
|
|
@@ -43,10 +39,6 @@ ParameterType = TypeVar(
|
|
|
43
39
|
BodyParameterType = TypeVar(
|
|
44
40
|
"BodyParameterType", bound=Union[BodyParameter, RequestBuilderBodyParameter]
|
|
45
41
|
)
|
|
46
|
-
RequestBuilderBodyParameterType = Union[
|
|
47
|
-
RequestBuilderBodyParameter, RequestBuilderMultipartBodyParameter
|
|
48
|
-
]
|
|
49
|
-
|
|
50
42
|
|
|
51
43
|
if TYPE_CHECKING:
|
|
52
44
|
from .code_model import CodeModel
|
|
@@ -314,7 +306,7 @@ class _ParameterListBase(
|
|
|
314
306
|
|
|
315
307
|
class _ParameterList(
|
|
316
308
|
_ParameterListBase[ # pylint: disable=unsubscriptable-object
|
|
317
|
-
Parameter,
|
|
309
|
+
Parameter, BodyParameter
|
|
318
310
|
]
|
|
319
311
|
):
|
|
320
312
|
"""Base Parameter class for the two operation ParameterLists"""
|
|
@@ -325,11 +317,9 @@ class _ParameterList(
|
|
|
325
317
|
|
|
326
318
|
@staticmethod
|
|
327
319
|
def body_parameter_creator() -> (
|
|
328
|
-
Callable[
|
|
329
|
-
[Dict[str, Any], "CodeModel"], Union[MultipartBodyParameter, BodyParameter]
|
|
330
|
-
]
|
|
320
|
+
Callable[[Dict[str, Any], "CodeModel"], BodyParameter]
|
|
331
321
|
):
|
|
332
|
-
return
|
|
322
|
+
return BodyParameter.from_yaml
|
|
333
323
|
|
|
334
324
|
@property
|
|
335
325
|
def implementation(self) -> str:
|
|
@@ -348,7 +338,7 @@ class ParameterList(_ParameterList):
|
|
|
348
338
|
|
|
349
339
|
class _RequestBuilderParameterList(
|
|
350
340
|
_ParameterListBase[ # pylint: disable=unsubscriptable-object
|
|
351
|
-
RequestBuilderParameter,
|
|
341
|
+
RequestBuilderParameter, RequestBuilderBodyParameter
|
|
352
342
|
]
|
|
353
343
|
):
|
|
354
344
|
"""_RequestBuilderParameterList is base parameter list for RequestBuilder classes"""
|
|
@@ -361,9 +351,9 @@ class _RequestBuilderParameterList(
|
|
|
361
351
|
|
|
362
352
|
@staticmethod
|
|
363
353
|
def body_parameter_creator() -> (
|
|
364
|
-
Callable[[Dict[str, Any], "CodeModel"],
|
|
354
|
+
Callable[[Dict[str, Any], "CodeModel"], RequestBuilderBodyParameter]
|
|
365
355
|
):
|
|
366
|
-
return
|
|
356
|
+
return RequestBuilderBodyParameter.from_yaml
|
|
367
357
|
|
|
368
358
|
@property
|
|
369
359
|
def implementation(self) -> str:
|
|
@@ -372,14 +362,14 @@ class _RequestBuilderParameterList(
|
|
|
372
362
|
@property
|
|
373
363
|
def unsorted_method_params(
|
|
374
364
|
self,
|
|
375
|
-
) -> List[Union[RequestBuilderParameter,
|
|
365
|
+
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameter]]:
|
|
376
366
|
# don't have access to client params in request builder
|
|
377
367
|
retval = [
|
|
378
368
|
p
|
|
379
369
|
for p in super().unsorted_method_params
|
|
380
370
|
if not (
|
|
381
371
|
p.location == ParameterLocation.BODY
|
|
382
|
-
and cast(
|
|
372
|
+
and cast(RequestBuilderBodyParameter, p).is_partial_body
|
|
383
373
|
)
|
|
384
374
|
]
|
|
385
375
|
retval.extend(
|
|
@@ -400,7 +390,7 @@ class _RequestBuilderParameterList(
|
|
|
400
390
|
@property
|
|
401
391
|
def constant(
|
|
402
392
|
self,
|
|
403
|
-
) -> List[Union[RequestBuilderParameter,
|
|
393
|
+
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameter]]:
|
|
404
394
|
"""All constant parameters"""
|
|
405
395
|
return [
|
|
406
396
|
p for p in super().constant if p.location != ParameterLocation.ENDPOINT_PATH
|
|
@@ -37,6 +37,9 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
37
37
|
if self.client_default_value is None:
|
|
38
38
|
self.client_default_value = self.type.client_default_value
|
|
39
39
|
self.flattened_names: List[str] = yaml_data.get("flattenedNames", [])
|
|
40
|
+
self.is_multipart_file_input: bool = yaml_data.get(
|
|
41
|
+
"isMultipartFileInput", False
|
|
42
|
+
)
|
|
40
43
|
|
|
41
44
|
@property
|
|
42
45
|
def pylint_disable(self) -> str:
|
|
@@ -94,12 +97,18 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
94
97
|
return self.is_discriminator and self.type.type == "enum"
|
|
95
98
|
|
|
96
99
|
def type_annotation(self, *, is_operation_file: bool = False) -> str:
|
|
100
|
+
types_type_annotation = self.type.type_annotation(
|
|
101
|
+
is_operation_file=is_operation_file
|
|
102
|
+
)
|
|
103
|
+
if self.is_multipart_file_input:
|
|
104
|
+
# we only support FileType or list of FileType
|
|
105
|
+
types_type_annotation = types_type_annotation.replace("bytes", "FileType")
|
|
97
106
|
if self.is_enum_discriminator:
|
|
98
107
|
# here we are the enum discriminator property on the base model
|
|
99
108
|
return "Literal[None]"
|
|
100
109
|
if self.optional and self.client_default_value is None:
|
|
101
|
-
return f"Optional[{
|
|
102
|
-
return
|
|
110
|
+
return f"Optional[{types_type_annotation}]"
|
|
111
|
+
return types_type_annotation
|
|
103
112
|
|
|
104
113
|
def get_declaration(self, value: Any = None) -> Any:
|
|
105
114
|
if self.is_enum_discriminator:
|
|
@@ -114,6 +123,8 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
114
123
|
client_default_value_declaration: Optional[str] = None,
|
|
115
124
|
description: Optional[str] = None,
|
|
116
125
|
) -> Any:
|
|
126
|
+
if self.is_multipart_file_input:
|
|
127
|
+
return "[filetype]" if self.type.type == "list" else "filetype"
|
|
117
128
|
if self.client_default_value:
|
|
118
129
|
client_default_value_declaration = self.get_declaration(
|
|
119
130
|
self.client_default_value
|
|
@@ -161,6 +172,8 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
161
172
|
"rest_discriminator" if self.is_discriminator else "rest_field",
|
|
162
173
|
ImportType.LOCAL,
|
|
163
174
|
)
|
|
175
|
+
if self.is_multipart_file_input:
|
|
176
|
+
file_import.add_submodule_import(".._vendor", "FileType", ImportType.LOCAL)
|
|
164
177
|
return file_import
|
|
165
178
|
|
|
166
179
|
@classmethod
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
7
7
|
from .parameter import (
|
|
8
8
|
ParameterLocation,
|
|
9
9
|
ParameterMethodLocation,
|
|
10
10
|
Parameter,
|
|
11
11
|
BodyParameter,
|
|
12
|
-
_MultipartBodyParameter,
|
|
13
12
|
)
|
|
14
13
|
from .base import BaseType
|
|
15
14
|
from .primitive_types import BinaryType, StringType
|
|
@@ -67,32 +66,6 @@ class RequestBuilderBodyParameter(BodyParameter):
|
|
|
67
66
|
return "_content"
|
|
68
67
|
|
|
69
68
|
|
|
70
|
-
class RequestBuilderMultipartBodyParameter(
|
|
71
|
-
_MultipartBodyParameter[ # pylint: disable=unsubscriptable-object
|
|
72
|
-
RequestBuilderBodyParameter
|
|
73
|
-
]
|
|
74
|
-
):
|
|
75
|
-
"""Multipart body parameter for Request BUilders"""
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def name_in_high_level_operation(self) -> str:
|
|
79
|
-
return f"_{self.client_name}"
|
|
80
|
-
|
|
81
|
-
@classmethod
|
|
82
|
-
def from_yaml(
|
|
83
|
-
cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
|
|
84
|
-
) -> "RequestBuilderMultipartBodyParameter":
|
|
85
|
-
return cls(
|
|
86
|
-
yaml_data=yaml_data,
|
|
87
|
-
code_model=code_model,
|
|
88
|
-
type=code_model.lookup_type(id(yaml_data["type"])),
|
|
89
|
-
entries=[
|
|
90
|
-
RequestBuilderBodyParameter.from_yaml(entry, code_model)
|
|
91
|
-
for entry in yaml_data["entries"]
|
|
92
|
-
],
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
69
|
class RequestBuilderParameter(Parameter):
|
|
97
70
|
"""Basic RequestBuilder Parameter."""
|
|
98
71
|
|
|
@@ -149,12 +122,3 @@ class RequestBuilderParameter(Parameter):
|
|
|
149
122
|
if self.implementation == "Client":
|
|
150
123
|
return f"self._config.{self.client_name}"
|
|
151
124
|
return self.client_name
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def get_request_body_parameter(
|
|
155
|
-
yaml_data: Dict[str, Any], code_model: "CodeModel"
|
|
156
|
-
) -> Union[RequestBuilderBodyParameter, RequestBuilderMultipartBodyParameter]:
|
|
157
|
-
"""Get body parameter for a request builder"""
|
|
158
|
-
if yaml_data.get("entries"):
|
|
159
|
-
return RequestBuilderMultipartBodyParameter.from_yaml(yaml_data, code_model)
|
|
160
|
-
return RequestBuilderBodyParameter.from_yaml(yaml_data, code_model)
|
|
@@ -26,7 +26,6 @@ from ..models import (
|
|
|
26
26
|
ParameterMethodLocation,
|
|
27
27
|
RequestBuilderBodyParameter,
|
|
28
28
|
OverloadedRequestBuilder,
|
|
29
|
-
MultipartBodyParameter,
|
|
30
29
|
Property,
|
|
31
30
|
RequestBuilderType,
|
|
32
31
|
CombinedType,
|
|
@@ -207,7 +206,7 @@ def _serialize_json_model_body(
|
|
|
207
206
|
|
|
208
207
|
def _serialize_multipart_body(builder: BuilderType) -> List[str]:
|
|
209
208
|
retval: List[str] = []
|
|
210
|
-
body_param =
|
|
209
|
+
body_param = builder.parameters.body_parameter
|
|
211
210
|
# we have to construct our form data before passing to the request as well
|
|
212
211
|
retval.append("# Construct form data")
|
|
213
212
|
retval.append(f"_{body_param.client_name} = {{")
|
|
@@ -349,7 +348,11 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
|
|
|
349
348
|
def param_description(self, builder: BuilderType) -> List[str]:
|
|
350
349
|
description_list: List[str] = []
|
|
351
350
|
for param in builder.parameters.method:
|
|
352
|
-
if
|
|
351
|
+
if (
|
|
352
|
+
not param.in_docstring
|
|
353
|
+
or param.hide_in_operation_signature
|
|
354
|
+
or param.method_location == ParameterMethodLocation.KWARG
|
|
355
|
+
):
|
|
353
356
|
continue
|
|
354
357
|
description_list.extend(
|
|
355
358
|
f":{param.description_keyword} {param.client_name}: {param.description}".replace(
|
|
@@ -700,19 +703,6 @@ class _OperationSerializer(
|
|
|
700
703
|
retval.append(_api_version_validation(builder))
|
|
701
704
|
return retval
|
|
702
705
|
|
|
703
|
-
def param_description(self, builder: OperationType) -> List[str]:
|
|
704
|
-
description_list = super().param_description(builder)
|
|
705
|
-
if builder.expose_stream_keyword and builder.has_response_body:
|
|
706
|
-
description_list.append(
|
|
707
|
-
":keyword bool stream: Whether to stream the response of this operation. "
|
|
708
|
-
"Defaults to False. You will have to context manage the returned stream."
|
|
709
|
-
)
|
|
710
|
-
if not self.code_model.options["version_tolerant"]:
|
|
711
|
-
description_list.append(
|
|
712
|
-
":keyword callable cls: A custom type or function that will be passed the direct response"
|
|
713
|
-
)
|
|
714
|
-
return description_list
|
|
715
|
-
|
|
716
706
|
def pop_kwargs_from_signature(self, builder: OperationType) -> List[str]:
|
|
717
707
|
kwargs_to_pop = builder.parameters.kwargs_to_pop
|
|
718
708
|
kwargs = self.parameter_serializer.pop_kwargs_from_signature(
|
|
@@ -761,17 +751,39 @@ class _OperationSerializer(
|
|
|
761
751
|
|
|
762
752
|
This function serializes the body params that need to be serialized.
|
|
763
753
|
"""
|
|
764
|
-
|
|
754
|
+
retval: List[str] = []
|
|
755
|
+
body_param = builder.parameters.body_parameter
|
|
765
756
|
if body_param.is_form_data:
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
757
|
+
model_type = cast(
|
|
758
|
+
ModelType,
|
|
759
|
+
(
|
|
760
|
+
body_param.type.target_model_subtype((JSONModelType, DPGModelType))
|
|
761
|
+
if isinstance(body_param.type, CombinedType)
|
|
762
|
+
else body_param.type
|
|
763
|
+
),
|
|
764
|
+
)
|
|
765
|
+
file_fields = [
|
|
766
|
+
p.wire_name for p in model_type.properties if p.is_multipart_file_input
|
|
773
767
|
]
|
|
774
|
-
|
|
768
|
+
data_fields = [
|
|
769
|
+
p.wire_name
|
|
770
|
+
for p in model_type.properties
|
|
771
|
+
if not p.is_multipart_file_input
|
|
772
|
+
]
|
|
773
|
+
retval.extend(
|
|
774
|
+
[
|
|
775
|
+
"_body = (",
|
|
776
|
+
f" {body_param.client_name}.as_dict()",
|
|
777
|
+
f" if isinstance({body_param.client_name}, _model_base.Model) else",
|
|
778
|
+
f" {body_param.client_name}",
|
|
779
|
+
")",
|
|
780
|
+
f"_file_fields: List[str] = {file_fields}",
|
|
781
|
+
f"_data_fields: List[str] = {data_fields}",
|
|
782
|
+
"_files, _data = prepare_multipart_form_data(_body, _file_fields, _data_fields)",
|
|
783
|
+
]
|
|
784
|
+
)
|
|
785
|
+
return retval
|
|
786
|
+
|
|
775
787
|
body_kwarg_name = builder.request_builder.parameters.body_parameter.client_name
|
|
776
788
|
send_xml = builder.parameters.body_parameter.type.is_xml
|
|
777
789
|
xml_serialization_ctxt = (
|
|
@@ -823,8 +835,8 @@ class _OperationSerializer(
|
|
|
823
835
|
) -> List[str]:
|
|
824
836
|
"""Create the body parameter before we pass it as either json or content to the request builder"""
|
|
825
837
|
retval = []
|
|
826
|
-
body_param =
|
|
827
|
-
if
|
|
838
|
+
body_param = builder.parameters.body_parameter
|
|
839
|
+
if body_param.entries:
|
|
828
840
|
return _serialize_multipart_body(builder)
|
|
829
841
|
body_kwarg_name = builder.request_builder.parameters.body_parameter.client_name
|
|
830
842
|
body_param_type = body_param.type
|
|
@@ -985,9 +997,13 @@ class _OperationSerializer(
|
|
|
985
997
|
f" {parameter.client_name}={parameter.name_in_high_level_operation},"
|
|
986
998
|
f"{' # type: ignore' if type_ignore else ''}"
|
|
987
999
|
)
|
|
988
|
-
if
|
|
989
|
-
|
|
1000
|
+
if builder.parameters.has_body and builder.parameters.body_parameter.entries:
|
|
1001
|
+
# this is for legacy
|
|
1002
|
+
client_name = builder.parameters.body_parameter.client_name
|
|
1003
|
+
retval.append(f" {client_name}=_{client_name},")
|
|
1004
|
+
elif request_builder.has_form_data_body:
|
|
990
1005
|
retval.append(" files=_files,")
|
|
1006
|
+
retval.append(" data=_data,")
|
|
991
1007
|
elif request_builder.overloads:
|
|
992
1008
|
seen_body_params = set()
|
|
993
1009
|
for overload in request_builder.overloads:
|
|
@@ -1543,25 +1559,6 @@ class _LROOperationSerializer(_OperationSerializer[LROOperationType]):
|
|
|
1543
1559
|
self.async_mode = async_mode
|
|
1544
1560
|
self.parameter_serializer = ParameterSerializer()
|
|
1545
1561
|
|
|
1546
|
-
def param_description(self, builder: LROOperationType) -> List[str]:
|
|
1547
|
-
retval = super().param_description(builder)
|
|
1548
|
-
retval.append(
|
|
1549
|
-
":keyword str continuation_token: A continuation token to restart a poller from a saved state."
|
|
1550
|
-
)
|
|
1551
|
-
retval.append(
|
|
1552
|
-
f":keyword polling: By default, your polling method will be {builder.get_polling_method(self.async_mode)}. "
|
|
1553
|
-
"Pass in False for this operation to not poll, or pass in your own initialized polling object for a"
|
|
1554
|
-
" personal polling strategy."
|
|
1555
|
-
)
|
|
1556
|
-
retval.append(
|
|
1557
|
-
f":paramtype polling: bool or ~{builder.get_base_polling_method_path(self.async_mode)}"
|
|
1558
|
-
)
|
|
1559
|
-
retval.append(
|
|
1560
|
-
":keyword int polling_interval: Default waiting time between two polls for LRO operations "
|
|
1561
|
-
"if no Retry-After header is present."
|
|
1562
|
-
)
|
|
1563
|
-
return retval
|
|
1564
|
-
|
|
1565
1562
|
def serialize_path(self, builder: LROOperationType) -> List[str]:
|
|
1566
1563
|
return self.parameter_serializer.serialize_path(
|
|
1567
1564
|
builder.parameters.path, self.serializer_name
|
|
@@ -130,18 +130,30 @@ class GeneralSerializer(BaseSerializer):
|
|
|
130
130
|
"MatchConditions",
|
|
131
131
|
ImportType.SDKCORE,
|
|
132
132
|
)
|
|
133
|
-
if
|
|
133
|
+
if (
|
|
134
|
+
self.code_model.has_form_data
|
|
135
|
+
and self.code_model.options["models_mode"] == "dpg"
|
|
136
|
+
):
|
|
137
|
+
file_import.add_submodule_import("typing", "IO", ImportType.STDLIB)
|
|
138
|
+
file_import.add_submodule_import("typing", "Tuple", ImportType.STDLIB)
|
|
134
139
|
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB)
|
|
140
|
+
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
|
|
141
|
+
file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
|
|
142
|
+
file_import.add_submodule_import("typing", "Sequence", ImportType.STDLIB)
|
|
143
|
+
file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB)
|
|
135
144
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
136
|
-
file_import.add_submodule_import("
|
|
137
|
-
file_import.add_submodule_import("io", "BytesIO", ImportType.STDLIB)
|
|
138
|
-
file_import.add_import("uuid", ImportType.STDLIB)
|
|
139
|
-
file_import.add_import("json", ImportType.STDLIB)
|
|
140
|
-
file_import.add_mutable_mapping_import()
|
|
141
|
-
file_import.add_submodule_import("._model_base", "Model", ImportType.LOCAL)
|
|
145
|
+
file_import.add_submodule_import("typing", "List", ImportType.STDLIB)
|
|
142
146
|
file_import.add_submodule_import(
|
|
143
|
-
"._model_base",
|
|
147
|
+
"._model_base",
|
|
148
|
+
"SdkJSONEncoder",
|
|
149
|
+
ImportType.LOCAL,
|
|
144
150
|
)
|
|
151
|
+
file_import.add_submodule_import(
|
|
152
|
+
"._model_base",
|
|
153
|
+
"Model",
|
|
154
|
+
ImportType.LOCAL,
|
|
155
|
+
)
|
|
156
|
+
file_import.add_import("json", ImportType.STDLIB)
|
|
145
157
|
|
|
146
158
|
return template.render(
|
|
147
159
|
code_model=self.code_model,
|
|
@@ -257,7 +257,10 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
257
257
|
args.append(f"visibility=[{v_list}]")
|
|
258
258
|
if prop.client_default_value is not None:
|
|
259
259
|
args.append(f"default={prop.client_default_value_declaration}")
|
|
260
|
-
|
|
260
|
+
|
|
261
|
+
if prop.is_multipart_file_input:
|
|
262
|
+
args.append("is_multipart_file_input=True")
|
|
263
|
+
elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
|
|
261
264
|
args.append(f'format="{prop.type.encode}"') # type: ignore
|
|
262
265
|
|
|
263
266
|
field = "rest_discriminator" if prop.is_discriminator else "rest_field"
|
|
@@ -32,8 +32,8 @@ SPECIAL_HEADER_SERIALIZATION: Dict[str, List[str]] = {
|
|
|
32
32
|
],
|
|
33
33
|
"repeatability-first-sent": [
|
|
34
34
|
"""if "Repeatability-First-Sent" not in _headers:""",
|
|
35
|
-
""" _headers["Repeatability-First-Sent"] = _SERIALIZER.serialize_data(
|
|
36
|
-
"rfc-1123")""",
|
|
35
|
+
""" _headers["Repeatability-First-Sent"] = _SERIALIZER.serialize_data(""",
|
|
36
|
+
""" datetime.datetime.now(datetime.timezone.utc), "rfc-1123")""",
|
|
37
37
|
],
|
|
38
38
|
"client-request-id": [],
|
|
39
39
|
"x-ms-client-request-id": [],
|
|
@@ -16,7 +16,7 @@ import base64
|
|
|
16
16
|
import re
|
|
17
17
|
import copy
|
|
18
18
|
import typing
|
|
19
|
-
import email
|
|
19
|
+
import email.utils
|
|
20
20
|
from datetime import datetime, date, time, timedelta, timezone
|
|
21
21
|
from json import JSONEncoder
|
|
22
22
|
import isodate
|
|
@@ -470,7 +470,13 @@ def _get_rest_field(
|
|
|
470
470
|
|
|
471
471
|
|
|
472
472
|
def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
|
|
473
|
-
|
|
473
|
+
if not rf:
|
|
474
|
+
return _serialize(value, None)
|
|
475
|
+
if rf._is_multipart_file_input:
|
|
476
|
+
return value
|
|
477
|
+
if rf._is_model:
|
|
478
|
+
return _deserialize(rf._type, value)
|
|
479
|
+
return _serialize(value, rf._format)
|
|
474
480
|
|
|
475
481
|
|
|
476
482
|
class Model(_MyMutableMapping):
|
|
@@ -567,7 +573,12 @@ class Model(_MyMutableMapping):
|
|
|
567
573
|
for k, v in self.items():
|
|
568
574
|
if exclude_readonly and k in readonly_props: # pyright: ignore[reportUnboundVariable]
|
|
569
575
|
continue
|
|
570
|
-
|
|
576
|
+
is_multipart_file_input = False
|
|
577
|
+
try:
|
|
578
|
+
is_multipart_file_input = next(rf for rf in self._attr_to_rest_field.values() if rf._rest_name == k)._is_multipart_file_input
|
|
579
|
+
except StopIteration:
|
|
580
|
+
pass
|
|
581
|
+
result[k] = v if is_multipart_file_input else Model._as_dict_value(v, exclude_readonly=exclude_readonly)
|
|
571
582
|
return result
|
|
572
583
|
|
|
573
584
|
@staticmethod
|
|
@@ -575,10 +586,10 @@ class Model(_MyMutableMapping):
|
|
|
575
586
|
if v is None or isinstance(v, _Null):
|
|
576
587
|
return None
|
|
577
588
|
if isinstance(v, (list, tuple, set)):
|
|
578
|
-
return
|
|
589
|
+
return type(v)(
|
|
579
590
|
Model._as_dict_value(x, exclude_readonly=exclude_readonly)
|
|
580
591
|
for x in v
|
|
581
|
-
|
|
592
|
+
)
|
|
582
593
|
if isinstance(v, dict):
|
|
583
594
|
return {
|
|
584
595
|
dk: Model._as_dict_value(dv, exclude_readonly=exclude_readonly)
|
|
@@ -625,7 +636,7 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=R0911, R0915,
|
|
|
625
636
|
|
|
626
637
|
# is it a literal?
|
|
627
638
|
try:
|
|
628
|
-
if annotation.__origin__
|
|
639
|
+
if annotation.__origin__ is typing.Literal:
|
|
629
640
|
return None
|
|
630
641
|
except AttributeError:
|
|
631
642
|
pass
|
|
@@ -779,6 +790,7 @@ class _RestField:
|
|
|
779
790
|
visibility: typing.Optional[typing.List[str]] = None,
|
|
780
791
|
default: typing.Any = _UNSET,
|
|
781
792
|
format: typing.Optional[str] = None,
|
|
793
|
+
is_multipart_file_input: bool = False,
|
|
782
794
|
):
|
|
783
795
|
self._type = type
|
|
784
796
|
self._rest_name_input = name
|
|
@@ -788,6 +800,7 @@ class _RestField:
|
|
|
788
800
|
self._is_model = False
|
|
789
801
|
self._default = default
|
|
790
802
|
self._format = format
|
|
803
|
+
self._is_multipart_file_input = is_multipart_file_input
|
|
791
804
|
|
|
792
805
|
@property
|
|
793
806
|
def _rest_name(self) -> str:
|
|
@@ -833,8 +846,9 @@ def rest_field(
|
|
|
833
846
|
visibility: typing.Optional[typing.List[str]] = None,
|
|
834
847
|
default: typing.Any = _UNSET,
|
|
835
848
|
format: typing.Optional[str] = None,
|
|
849
|
+
is_multipart_file_input: bool = False,
|
|
836
850
|
) -> typing.Any:
|
|
837
|
-
return _RestField(name=name, type=type, visibility=visibility, default=default, format=format)
|
|
851
|
+
return _RestField(name=name, type=type, visibility=visibility, default=default, format=format, is_multipart_file_input=is_multipart_file_input)
|
|
838
852
|
|
|
839
853
|
|
|
840
854
|
def rest_discriminator(
|
|
@@ -65,38 +65,42 @@ def prep_if_none_match(etag: Optional[str], match_condition: Optional[MatchCondi
|
|
|
65
65
|
return "*"
|
|
66
66
|
return None
|
|
67
67
|
{% endif %}
|
|
68
|
-
{% if code_model.has_form_data %}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
super().__init__(*args, **kwargs)
|
|
72
|
-
self.name = name
|
|
68
|
+
{% if code_model.has_form_data and code_model.options["models_mode"] == "dpg" %}
|
|
69
|
+
# file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)`
|
|
70
|
+
FileContent = Union[str, bytes, IO[str], IO[bytes]]
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
FileType = Union[
|
|
73
|
+
# file (or bytes)
|
|
74
|
+
FileContent,
|
|
75
|
+
# (filename, file (or bytes))
|
|
76
|
+
Tuple[Optional[str], FileContent],
|
|
77
|
+
# (filename, file (or bytes), content_type)
|
|
78
|
+
Tuple[Optional[str], FileContent, Optional[str]],
|
|
79
|
+
]
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
if isinstance(data, (list, tuple, dict, Model)):
|
|
81
|
-
return json.dumps(data, cls=SdkJSONEncoder, exclude_readonly=True)
|
|
82
|
-
return data
|
|
81
|
+
FilesType = Union[Mapping[str, FileType], Sequence[Tuple[str, FileType]]]
|
|
83
82
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
def serialize_multipart_data_entry(data_entry: Any) -> Any:
|
|
84
|
+
if isinstance(data_entry, (list, tuple, dict, Model)):
|
|
85
|
+
return json.dumps(data_entry, cls=SdkJSONEncoder, exclude_readonly=True)
|
|
86
|
+
return data_entry
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
:
|
|
90
|
-
|
|
91
|
-
:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
def prepare_multipart_form_data(
|
|
89
|
+
body: Mapping[str, Any], multipart_fields: List[str], data_fields: List[str]
|
|
90
|
+
) -> Tuple[List[FileType], Dict[str, Any]]:
|
|
91
|
+
files: List[FileType] = []
|
|
92
|
+
data: Dict[str, Any] = {}
|
|
93
|
+
for multipart_field in multipart_fields:
|
|
94
|
+
multipart_entry = body.get(multipart_field)
|
|
95
|
+
if isinstance(multipart_entry, list):
|
|
96
|
+
files.extend([(multipart_field, e) for e in multipart_entry ])
|
|
97
|
+
elif multipart_entry:
|
|
98
|
+
files.append((multipart_field, multipart_entry))
|
|
99
|
+
|
|
100
|
+
for data_field in data_fields:
|
|
101
|
+
data_entry = body.get(data_field)
|
|
102
|
+
if data_entry:
|
|
103
|
+
data[data_field] = serialize_multipart_data_entry(data_entry)
|
|
104
|
+
|
|
105
|
+
return files, data
|
|
106
|
+
{% endif %}
|
package/package.json
CHANGED
package/requirements.txt
CHANGED
package/run_cadl.py
CHANGED
|
@@ -22,7 +22,7 @@ if __name__ == "__main__":
|
|
|
22
22
|
env_builder = venv.EnvBuilder(with_pip=True)
|
|
23
23
|
venv_context = env_builder.ensure_directories(venv_path)
|
|
24
24
|
|
|
25
|
-
if "--debug" in sys.argv:
|
|
25
|
+
if "--debug" in sys.argv or "--debug=true" in sys.argv:
|
|
26
26
|
try:
|
|
27
27
|
import debugpy # pylint: disable=import-outside-toplevel
|
|
28
28
|
except ImportError:
|