Skip to content

Commit f1caadc

Browse files
csymeonides-mfConstantinos Symeonides
authored andcommitted
feat!:Allow more types in multipart payloads by defaulting to JSON for complex types (#372). Thanks @csymeonides-mf!
* fix: Detect File fields correctly * fix: Optional File field caused missing import error * fix: Non-string multipart fields must be stringified * chore: Reformat * fix: Put non-file fields as tuples in the files dict * refactor: Avoid runtime type checks * fix: Declaring types is necessary * fix: Remove dead code * fix: Non-JSON non-file multipart props need to be text/plain * refactor: Avoid breaking change Co-authored-by: Constantinos Symeonides <[email protected]>
1 parent 56198ee commit f1caadc

26 files changed

+750
-141
lines changed

end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ...client import Client
66
from ...models.body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost
77
from ...models.http_validation_error import HTTPValidationError
8-
from ...types import UNSET, File, Response, Unset
8+
from ...types import UNSET, Response, Unset
99

1010

1111
def _get_kwargs(
@@ -22,21 +22,14 @@ def _get_kwargs(
2222
if keep_alive is not UNSET:
2323
headers["keep-alive"] = keep_alive
2424

25-
files = {}
26-
data = {}
27-
for key, value in multipart_data.to_dict().items():
28-
if isinstance(value, File):
29-
files[key] = value
30-
else:
31-
data[key] = value
25+
multipart_multipart_data = multipart_data.to_multipart()
3226

3327
return {
3428
"url": url,
3529
"headers": headers,
3630
"cookies": cookies,
3731
"timeout": client.get_timeout(),
38-
"files": files,
39-
"data": data,
32+
"files": multipart_multipart_data,
4033
}
4134

4235

end_to_end_tests/golden-record/my_test_api_client/models/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
from .an_int_enum import AnIntEnum
1010
from .another_all_of_sub_model import AnotherAllOfSubModel
1111
from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost
12+
from .body_upload_file_tests_upload_post_additional_property import BodyUploadFileTestsUploadPostAdditionalProperty
13+
from .body_upload_file_tests_upload_post_some_nullable_object import BodyUploadFileTestsUploadPostSomeNullableObject
14+
from .body_upload_file_tests_upload_post_some_object import BodyUploadFileTestsUploadPostSomeObject
15+
from .body_upload_file_tests_upload_post_some_optional_object import BodyUploadFileTestsUploadPostSomeOptionalObject
1216
from .different_enum import DifferentEnum
1317
from .free_form_model import FreeFormModel
1418
from .http_validation_error import HTTPValidationError

end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py

+179-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1+
import json
12
from io import BytesIO
2-
from typing import Any, Dict, Type, TypeVar, Union
3+
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union, cast
34

45
import attr
56

6-
from ..types import UNSET, File, Unset
7+
from ..models.body_upload_file_tests_upload_post_additional_property import (
8+
BodyUploadFileTestsUploadPostAdditionalProperty,
9+
)
10+
from ..models.body_upload_file_tests_upload_post_some_nullable_object import (
11+
BodyUploadFileTestsUploadPostSomeNullableObject,
12+
)
13+
from ..models.body_upload_file_tests_upload_post_some_object import BodyUploadFileTestsUploadPostSomeObject
14+
from ..models.body_upload_file_tests_upload_post_some_optional_object import (
15+
BodyUploadFileTestsUploadPostSomeOptionalObject,
16+
)
17+
from ..models.different_enum import DifferentEnum
18+
from ..types import UNSET, File, FileJsonType, Unset
719

820
T = TypeVar("T", bound="BodyUploadFileTestsUploadPost")
921

@@ -13,21 +25,122 @@ class BodyUploadFileTestsUploadPost:
1325
""" """
1426

1527
some_file: File
28+
some_object: BodyUploadFileTestsUploadPostSomeObject
29+
some_nullable_object: Optional[BodyUploadFileTestsUploadPostSomeNullableObject]
30+
some_optional_file: Union[Unset, File] = UNSET
1631
some_string: Union[Unset, str] = "some_default_string"
32+
some_number: Union[Unset, float] = UNSET
33+
some_array: Union[Unset, List[float]] = UNSET
34+
some_optional_object: Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject] = UNSET
35+
some_enum: Union[Unset, DifferentEnum] = UNSET
36+
additional_properties: Dict[str, BodyUploadFileTestsUploadPostAdditionalProperty] = attr.ib(
37+
init=False, factory=dict
38+
)
1739

1840
def to_dict(self) -> Dict[str, Any]:
1941
some_file = self.some_file.to_tuple()
2042

43+
some_object = self.some_object.to_dict()
44+
45+
some_optional_file: Union[Unset, FileJsonType] = UNSET
46+
if not isinstance(self.some_optional_file, Unset):
47+
some_optional_file = self.some_optional_file.to_tuple()
48+
2149
some_string = self.some_string
50+
some_number = self.some_number
51+
some_array: Union[Unset, List[float]] = UNSET
52+
if not isinstance(self.some_array, Unset):
53+
some_array = self.some_array
54+
55+
some_optional_object: Union[Unset, Dict[str, Any]] = UNSET
56+
if not isinstance(self.some_optional_object, Unset):
57+
some_optional_object = self.some_optional_object.to_dict()
58+
59+
some_nullable_object = self.some_nullable_object.to_dict() if self.some_nullable_object else None
60+
61+
some_enum: Union[Unset, str] = UNSET
62+
if not isinstance(self.some_enum, Unset):
63+
some_enum = self.some_enum.value
2264

2365
field_dict: Dict[str, Any] = {}
66+
for prop_name, prop in self.additional_properties.items():
67+
field_dict[prop_name] = prop.to_dict()
68+
2469
field_dict.update(
2570
{
2671
"some_file": some_file,
72+
"some_object": some_object,
73+
"some_nullable_object": some_nullable_object,
2774
}
2875
)
76+
if some_optional_file is not UNSET:
77+
field_dict["some_optional_file"] = some_optional_file
2978
if some_string is not UNSET:
3079
field_dict["some_string"] = some_string
80+
if some_number is not UNSET:
81+
field_dict["some_number"] = some_number
82+
if some_array is not UNSET:
83+
field_dict["some_array"] = some_array
84+
if some_optional_object is not UNSET:
85+
field_dict["some_optional_object"] = some_optional_object
86+
if some_enum is not UNSET:
87+
field_dict["some_enum"] = some_enum
88+
89+
return field_dict
90+
91+
def to_multipart(self) -> Dict[str, Any]:
92+
some_file = self.some_file.to_tuple()
93+
94+
some_object = (None, json.dumps(self.some_object.to_dict()), "application/json")
95+
96+
some_optional_file: Union[Unset, FileJsonType] = UNSET
97+
if not isinstance(self.some_optional_file, Unset):
98+
some_optional_file = self.some_optional_file.to_tuple()
99+
100+
some_string = self.some_string if self.some_string is UNSET else (None, str(self.some_string), "text/plain")
101+
some_number = self.some_number if self.some_number is UNSET else (None, str(self.some_number), "text/plain")
102+
some_array: Union[Unset, Tuple[None, str, str]] = UNSET
103+
if not isinstance(self.some_array, Unset):
104+
_temp_some_array = self.some_array
105+
some_array = (None, json.dumps(_temp_some_array), "application/json")
106+
107+
some_optional_object: Union[Unset, Tuple[None, str, str]] = UNSET
108+
if not isinstance(self.some_optional_object, Unset):
109+
some_optional_object = (None, json.dumps(self.some_optional_object.to_dict()), "application/json")
110+
111+
some_nullable_object = (
112+
(None, json.dumps(self.some_nullable_object.to_dict()), "application/json")
113+
if self.some_nullable_object
114+
else None
115+
)
116+
117+
some_enum: Union[Unset, Tuple[None, str, str]] = UNSET
118+
if not isinstance(self.some_enum, Unset):
119+
some_enum = (None, str(self.some_enum.value), "text/plain")
120+
121+
field_dict: Dict[str, Any] = {}
122+
for prop_name, prop in self.additional_properties.items():
123+
field_dict[prop_name] = (None, json.dumps(prop.to_dict()), "application/json")
124+
125+
field_dict.update(
126+
{
127+
"some_file": some_file,
128+
"some_object": some_object,
129+
"some_nullable_object": some_nullable_object,
130+
}
131+
)
132+
if some_optional_file is not UNSET:
133+
field_dict["some_optional_file"] = some_optional_file
134+
if some_string is not UNSET:
135+
field_dict["some_string"] = some_string
136+
if some_number is not UNSET:
137+
field_dict["some_number"] = some_number
138+
if some_array is not UNSET:
139+
field_dict["some_array"] = some_array
140+
if some_optional_object is not UNSET:
141+
field_dict["some_optional_object"] = some_optional_object
142+
if some_enum is not UNSET:
143+
field_dict["some_enum"] = some_enum
31144

32145
return field_dict
33146

@@ -36,11 +149,75 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
36149
d = src_dict.copy()
37150
some_file = File(payload=BytesIO(d.pop("some_file")))
38151

152+
some_object = BodyUploadFileTestsUploadPostSomeObject.from_dict(d.pop("some_object"))
153+
154+
_some_optional_file = d.pop("some_optional_file", UNSET)
155+
some_optional_file: Union[Unset, File]
156+
if isinstance(_some_optional_file, Unset):
157+
some_optional_file = UNSET
158+
else:
159+
some_optional_file = File(payload=BytesIO(_some_optional_file))
160+
39161
some_string = d.pop("some_string", UNSET)
40162

163+
some_number = d.pop("some_number", UNSET)
164+
165+
some_array = cast(List[float], d.pop("some_array", UNSET))
166+
167+
_some_optional_object = d.pop("some_optional_object", UNSET)
168+
some_optional_object: Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]
169+
if isinstance(_some_optional_object, Unset):
170+
some_optional_object = UNSET
171+
else:
172+
some_optional_object = BodyUploadFileTestsUploadPostSomeOptionalObject.from_dict(_some_optional_object)
173+
174+
_some_nullable_object = d.pop("some_nullable_object")
175+
some_nullable_object: Optional[BodyUploadFileTestsUploadPostSomeNullableObject]
176+
if _some_nullable_object is None:
177+
some_nullable_object = None
178+
else:
179+
some_nullable_object = BodyUploadFileTestsUploadPostSomeNullableObject.from_dict(_some_nullable_object)
180+
181+
_some_enum = d.pop("some_enum", UNSET)
182+
some_enum: Union[Unset, DifferentEnum]
183+
if isinstance(_some_enum, Unset):
184+
some_enum = UNSET
185+
else:
186+
some_enum = DifferentEnum(_some_enum)
187+
41188
body_upload_file_tests_upload_post = cls(
42189
some_file=some_file,
190+
some_object=some_object,
191+
some_optional_file=some_optional_file,
43192
some_string=some_string,
193+
some_number=some_number,
194+
some_array=some_array,
195+
some_optional_object=some_optional_object,
196+
some_nullable_object=some_nullable_object,
197+
some_enum=some_enum,
44198
)
45199

200+
additional_properties = {}
201+
for prop_name, prop_dict in d.items():
202+
additional_property = BodyUploadFileTestsUploadPostAdditionalProperty.from_dict(prop_dict)
203+
204+
additional_properties[prop_name] = additional_property
205+
206+
body_upload_file_tests_upload_post.additional_properties = additional_properties
46207
return body_upload_file_tests_upload_post
208+
209+
@property
210+
def additional_keys(self) -> List[str]:
211+
return list(self.additional_properties.keys())
212+
213+
def __getitem__(self, key: str) -> BodyUploadFileTestsUploadPostAdditionalProperty:
214+
return self.additional_properties[key]
215+
216+
def __setitem__(self, key: str, value: BodyUploadFileTestsUploadPostAdditionalProperty) -> None:
217+
self.additional_properties[key] = value
218+
219+
def __delitem__(self, key: str) -> None:
220+
del self.additional_properties[key]
221+
222+
def __contains__(self, key: str) -> bool:
223+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, Dict, List, Type, TypeVar, Union
2+
3+
import attr
4+
5+
from ..types import UNSET, Unset
6+
7+
T = TypeVar("T", bound="BodyUploadFileTestsUploadPostAdditionalProperty")
8+
9+
10+
@attr.s(auto_attribs=True)
11+
class BodyUploadFileTestsUploadPostAdditionalProperty:
12+
""" """
13+
14+
foo: Union[Unset, str] = UNSET
15+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
16+
17+
def to_dict(self) -> Dict[str, Any]:
18+
foo = self.foo
19+
20+
field_dict: Dict[str, Any] = {}
21+
field_dict.update(self.additional_properties)
22+
field_dict.update({})
23+
if foo is not UNSET:
24+
field_dict["foo"] = foo
25+
26+
return field_dict
27+
28+
@classmethod
29+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
30+
d = src_dict.copy()
31+
foo = d.pop("foo", UNSET)
32+
33+
body_upload_file_tests_upload_post_additional_property = cls(
34+
foo=foo,
35+
)
36+
37+
body_upload_file_tests_upload_post_additional_property.additional_properties = d
38+
return body_upload_file_tests_upload_post_additional_property
39+
40+
@property
41+
def additional_keys(self) -> List[str]:
42+
return list(self.additional_properties.keys())
43+
44+
def __getitem__(self, key: str) -> Any:
45+
return self.additional_properties[key]
46+
47+
def __setitem__(self, key: str, value: Any) -> None:
48+
self.additional_properties[key] = value
49+
50+
def __delitem__(self, key: str) -> None:
51+
del self.additional_properties[key]
52+
53+
def __contains__(self, key: str) -> bool:
54+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, Dict, List, Type, TypeVar, Union
2+
3+
import attr
4+
5+
from ..types import UNSET, Unset
6+
7+
T = TypeVar("T", bound="BodyUploadFileTestsUploadPostSomeNullableObject")
8+
9+
10+
@attr.s(auto_attribs=True)
11+
class BodyUploadFileTestsUploadPostSomeNullableObject:
12+
""" """
13+
14+
bar: Union[Unset, str] = UNSET
15+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
16+
17+
def to_dict(self) -> Dict[str, Any]:
18+
bar = self.bar
19+
20+
field_dict: Dict[str, Any] = {}
21+
field_dict.update(self.additional_properties)
22+
field_dict.update({})
23+
if bar is not UNSET:
24+
field_dict["bar"] = bar
25+
26+
return field_dict
27+
28+
@classmethod
29+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
30+
d = src_dict.copy()
31+
bar = d.pop("bar", UNSET)
32+
33+
body_upload_file_tests_upload_post_some_nullable_object = cls(
34+
bar=bar,
35+
)
36+
37+
body_upload_file_tests_upload_post_some_nullable_object.additional_properties = d
38+
return body_upload_file_tests_upload_post_some_nullable_object
39+
40+
@property
41+
def additional_keys(self) -> List[str]:
42+
return list(self.additional_properties.keys())
43+
44+
def __getitem__(self, key: str) -> Any:
45+
return self.additional_properties[key]
46+
47+
def __setitem__(self, key: str, value: Any) -> None:
48+
self.additional_properties[key] = value
49+
50+
def __delitem__(self, key: str) -> None:
51+
del self.additional_properties[key]
52+
53+
def __contains__(self, key: str) -> bool:
54+
return key in self.additional_properties

0 commit comments

Comments
 (0)