Skip to content

Commit 5bd5ce6

Browse files
committed
WIP Fix for #926
1 parent 516e5af commit 5bd5ce6

19 files changed

+261
-81
lines changed

end_to_end_tests/baseline_openapi_3.0.json

+30-16
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,9 @@
14151415
},
14161416
"/naming/mixed-case": {
14171417
"get": {
1418-
"tags": ["naming"],
1418+
"tags": [
1419+
"naming"
1420+
],
14191421
"operationId": "mixed_case",
14201422
"parameters": [
14211423
{
@@ -1436,30 +1438,32 @@
14361438
}
14371439
],
14381440
"responses": {
1439-
"200": {
1440-
"description": "Successful response",
1441-
"content": {
1442-
"application/json": {
1443-
"schema": {
1444-
"type": "object",
1445-
"properties": {
1446-
"mixed_case": {
1447-
"type": "string"
1448-
},
1449-
"mixedCase": {
1450-
"type": "string"
1451-
}
1441+
"200": {
1442+
"description": "Successful response",
1443+
"content": {
1444+
"application/json": {
1445+
"schema": {
1446+
"type": "object",
1447+
"properties": {
1448+
"mixed_case": {
1449+
"type": "string"
1450+
},
1451+
"mixedCase": {
1452+
"type": "string"
14521453
}
14531454
}
14541455
}
14551456
}
14561457
}
1458+
}
14571459
}
14581460
}
14591461
},
14601462
"/naming/{hyphen-in-path}": {
14611463
"get": {
1462-
"tags": ["naming"],
1464+
"tags": [
1465+
"naming"
1466+
],
14631467
"operationId": "hyphen_in_path",
14641468
"parameters": [
14651469
{
@@ -1833,7 +1837,8 @@
18331837
"required": [
18341838
"some_file",
18351839
"some_object",
1836-
"some_nullable_object"
1840+
"some_nullable_object",
1841+
"some_required_number"
18371842
],
18381843
"type": "object",
18391844
"properties": {
@@ -1866,6 +1871,15 @@
18661871
"title": "Some Number",
18671872
"type": "number"
18681873
},
1874+
"some_nullable_number": {
1875+
"title": "Some Nullable Number",
1876+
"type": "number",
1877+
"nullable": true
1878+
},
1879+
"some_required_number": {
1880+
"title": "Some Required Number",
1881+
"type": "number"
1882+
},
18691883
"some_array": {
18701884
"title": "Some Array",
18711885
"nullable": true,

end_to_end_tests/baseline_openapi_3.1.yaml

+10-1
Original file line numberDiff line numberDiff line change
@@ -1847,7 +1847,8 @@ info:
18471847
"required": [
18481848
"some_file",
18491849
"some_object",
1850-
"some_nullable_object"
1850+
"some_nullable_object",
1851+
"some_required_number"
18511852
],
18521853
"type": "object",
18531854
"properties": {
@@ -1880,6 +1881,14 @@ info:
18801881
"title": "Some Number",
18811882
"type": "number"
18821883
},
1884+
"some_nullable_number": {
1885+
"title": "Some Nullable Number",
1886+
"type": [ "number", "null" ]
1887+
},
1888+
"some_required_number": {
1889+
"title": "Some Number",
1890+
"type": "number"
1891+
},
18831892
"some_array": {
18841893
"title": "Some Array",
18851894
"type": [ "array", "null" ],

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

+49-4
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,30 @@ class BodyUploadFileTestsUploadPost:
3232
"""
3333
Attributes:
3434
some_file (File):
35+
some_required_number (float):
3536
some_object (BodyUploadFileTestsUploadPostSomeObject):
3637
some_nullable_object (Union['BodyUploadFileTestsUploadPostSomeNullableObject', None]):
3738
some_optional_file (Union[Unset, File]):
3839
some_string (Union[Unset, str]): Default: 'some_default_string'.
3940
a_datetime (Union[Unset, datetime.datetime]):
4041
a_date (Union[Unset, datetime.date]):
4142
some_number (Union[Unset, float]):
43+
some_nullable_number (Union[None, Unset, float]):
4244
some_array (Union[List['AFormData'], None, Unset]):
4345
some_optional_object (Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]):
4446
some_enum (Union[Unset, DifferentEnum]): An enumeration.
4547
"""
4648

4749
some_file: File
50+
some_required_number: float
4851
some_object: "BodyUploadFileTestsUploadPostSomeObject"
4952
some_nullable_object: Union["BodyUploadFileTestsUploadPostSomeNullableObject", None]
5053
some_optional_file: Union[Unset, File] = UNSET
5154
some_string: Union[Unset, str] = "some_default_string"
5255
a_datetime: Union[Unset, datetime.datetime] = UNSET
5356
a_date: Union[Unset, datetime.date] = UNSET
5457
some_number: Union[Unset, float] = UNSET
58+
some_nullable_number: Union[None, Unset, float] = UNSET
5559
some_array: Union[List["AFormData"], None, Unset] = UNSET
5660
some_optional_object: Union[Unset, "BodyUploadFileTestsUploadPostSomeOptionalObject"] = UNSET
5761
some_enum: Union[Unset, DifferentEnum] = UNSET
@@ -66,6 +70,8 @@ def to_dict(self) -> Dict[str, Any]:
6670

6771
some_file = self.some_file.to_tuple()
6872

73+
some_required_number = self.some_required_number
74+
6975
some_object = self.some_object.to_dict()
7076

7177
some_nullable_object: Union[Dict[str, Any], None]
@@ -90,6 +96,12 @@ def to_dict(self) -> Dict[str, Any]:
9096

9197
some_number = self.some_number
9298

99+
some_nullable_number: Union[None, Unset, float]
100+
if isinstance(self.some_nullable_number, Unset):
101+
some_nullable_number = UNSET
102+
else:
103+
some_nullable_number = self.some_nullable_number
104+
93105
some_array: Union[List[Dict[str, Any]], None, Unset]
94106
if isinstance(self.some_array, Unset):
95107
some_array = UNSET
@@ -116,6 +128,7 @@ def to_dict(self) -> Dict[str, Any]:
116128
field_dict.update(
117129
{
118130
"some_file": some_file,
131+
"some_required_number": some_required_number,
119132
"some_object": some_object,
120133
"some_nullable_object": some_nullable_object,
121134
}
@@ -130,6 +143,8 @@ def to_dict(self) -> Dict[str, Any]:
130143
field_dict["a_date"] = a_date
131144
if some_number is not UNSET:
132145
field_dict["some_number"] = some_number
146+
if some_nullable_number is not UNSET:
147+
field_dict["some_nullable_number"] = some_nullable_number
133148
if some_array is not UNSET:
134149
field_dict["some_array"] = some_array
135150
if some_optional_object is not UNSET:
@@ -142,13 +157,16 @@ def to_dict(self) -> Dict[str, Any]:
142157
def to_multipart(self) -> Dict[str, Any]:
143158
some_file = self.some_file.to_tuple()
144159

160+
some_required_number = (None, str(self.some_required_number).encode(), "text/plain")
161+
145162
some_object = (None, json.dumps(self.some_object.to_dict()).encode(), "application/json")
146163

147-
some_nullable_object: Union[None, Tuple[None, bytes, str]]
164+
some_nullable_object: Tuple[None, bytes, str]
165+
148166
if isinstance(self.some_nullable_object, BodyUploadFileTestsUploadPostSomeNullableObject):
149167
some_nullable_object = (None, json.dumps(self.some_nullable_object.to_dict()).encode(), "application/json")
150168
else:
151-
some_nullable_object = self.some_nullable_object
169+
some_nullable_object = (None, str(self.some_nullable_object).encode(), "text/plain")
152170

153171
some_optional_file: Union[Unset, FileJsonType] = UNSET
154172
if not isinstance(self.some_optional_file, Unset):
@@ -174,7 +192,18 @@ def to_multipart(self) -> Dict[str, Any]:
174192
else (None, str(self.some_number).encode(), "text/plain")
175193
)
176194

177-
some_array: Union[None, Tuple[None, bytes, str], Unset]
195+
some_nullable_number: Union[Tuple[None, bytes, str], Unset]
196+
197+
if isinstance(self.some_nullable_number, Unset):
198+
some_nullable_number = UNSET
199+
elif isinstance(self.some_nullable_number, float):
200+
some_nullable_number = (None, str(self.some_nullable_number).encode(), "text/plain")
201+
202+
else:
203+
some_nullable_number = (None, str(self.some_nullable_number).encode(), "text/plain")
204+
205+
some_array: Union[Tuple[None, bytes, str], Unset]
206+
178207
if isinstance(self.some_array, Unset):
179208
some_array = UNSET
180209
elif isinstance(self.some_array, list):
@@ -185,7 +214,7 @@ def to_multipart(self) -> Dict[str, Any]:
185214
some_array = (None, json.dumps(_temp_some_array).encode(), "application/json")
186215

187216
else:
188-
some_array = self.some_array
217+
some_array = (None, str(self.some_array).encode(), "text/plain")
189218

190219
some_optional_object: Union[Unset, Tuple[None, bytes, str]] = UNSET
191220
if not isinstance(self.some_optional_object, Unset):
@@ -201,6 +230,7 @@ def to_multipart(self) -> Dict[str, Any]:
201230
field_dict.update(
202231
{
203232
"some_file": some_file,
233+
"some_required_number": some_required_number,
204234
"some_object": some_object,
205235
"some_nullable_object": some_nullable_object,
206236
}
@@ -215,6 +245,8 @@ def to_multipart(self) -> Dict[str, Any]:
215245
field_dict["a_date"] = a_date
216246
if some_number is not UNSET:
217247
field_dict["some_number"] = some_number
248+
if some_nullable_number is not UNSET:
249+
field_dict["some_nullable_number"] = some_nullable_number
218250
if some_array is not UNSET:
219251
field_dict["some_array"] = some_array
220252
if some_optional_object is not UNSET:
@@ -241,6 +273,8 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
241273
d = src_dict.copy()
242274
some_file = File(payload=BytesIO(d.pop("some_file")))
243275

276+
some_required_number = d.pop("some_required_number")
277+
244278
some_object = BodyUploadFileTestsUploadPostSomeObject.from_dict(d.pop("some_object"))
245279

246280
def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploadPostSomeNullableObject", None]:
@@ -283,6 +317,15 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa
283317

284318
some_number = d.pop("some_number", UNSET)
285319

320+
def _parse_some_nullable_number(data: object) -> Union[None, Unset, float]:
321+
if data is None:
322+
return data
323+
if isinstance(data, Unset):
324+
return data
325+
return cast(Union[None, Unset, float], data)
326+
327+
some_nullable_number = _parse_some_nullable_number(d.pop("some_nullable_number", UNSET))
328+
286329
def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]:
287330
if data is None:
288331
return data
@@ -321,13 +364,15 @@ def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]:
321364

322365
body_upload_file_tests_upload_post = cls(
323366
some_file=some_file,
367+
some_required_number=some_required_number,
324368
some_object=some_object,
325369
some_nullable_object=some_nullable_object,
326370
some_optional_file=some_optional_file,
327371
some_string=some_string,
328372
a_datetime=a_datetime,
329373
a_date=a_date,
330374
some_number=some_number,
375+
some_nullable_number=some_nullable_number,
331376
some_array=some_array,
332377
some_optional_object=some_optional_object,
333378
some_enum=some_enum,

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ def to_multipart(self) -> Dict[str, Any]:
3333
a = self.a if isinstance(self.a, Unset) else (None, str(self.a).encode(), "text/plain")
3434

3535
field_dict: Dict[str, Any] = {}
36-
field_dict.update(
37-
{key: (None, str(value).encode(), "text/plain") for key, value in self.additional_properties.items()}
38-
)
36+
field_dict.update(self.additional_properties)
3937
field_dict.update({})
4038
if a is not UNSET:
4139
field_dict["a"] = a

integration-tests/integration_tests/models/post_body_multipart_body.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ def to_dict(self) -> Dict[str, Any]:
4545
return field_dict
4646

4747
def to_multipart(self) -> Dict[str, Any]:
48-
a_string = (
49-
self.a_string if isinstance(self.a_string, Unset) else (None, str(self.a_string).encode(), "text/plain")
50-
)
48+
a_string = (None, str(self.a_string).encode(), "text/plain")
5149

5250
file = self.file.to_tuple()
5351

@@ -58,9 +56,7 @@ def to_multipart(self) -> Dict[str, Any]:
5856
)
5957

6058
field_dict: Dict[str, Any] = {}
61-
field_dict.update(
62-
{key: (None, str(value).encode(), "text/plain") for key, value in self.additional_properties.items()}
63-
)
59+
field_dict.update(self.additional_properties)
6460
field_dict.update(
6561
{
6662
"a_string": a_string,

openapi_python_client/parser/properties/protocol.py

+2
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def get_type_string(
107107
"""
108108
if json:
109109
type_string = self.get_base_json_type_string(quoted=quoted)
110+
elif multipart:
111+
type_string = "Tuple[None, bytes, str]"
110112
else:
111113
type_string = self.get_base_type_string(quoted=quoted)
112114

openapi_python_client/templates/endpoint_macros.py.jinja

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
8383
{% macro multipart_body(body, destination) %}
8484
{% set property = body.prop %}
8585
{% import "property_templates/" + property.template as prop_template %}
86-
{% if prop_template.transform_multipart %}
87-
{{ prop_template.transform_multipart(property, property.python_name, destination) }}
86+
{% if prop_template.transform_multipart_body %}
87+
{{ prop_template.transform_multipart_body(property, property.python_name, destination) }}
8888
{% endif %}
8989
{% endmacro %}
9090

openapi_python_client/templates/model.py.jinja

+9-11
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ class {{ class_name }}:
8080
{% macro _to_dict(multipart=False) %}
8181
{% for property in model.required_properties + model.optional_properties %}
8282
{% import "property_templates/" + property.template as prop_template %}
83-
{% if prop_template.transform %}
84-
{{ prop_template.transform(property, "self." + property.python_name, property.python_name, multipart=multipart) }}
85-
{% elif multipart %}
86-
{{ property.python_name }} = self.{{ property.python_name }} if isinstance(self.{{ property.python_name }}, Unset) else (None, str(self.{{ property.python_name }}).encode(), "text/plain")
83+
{% if multipart %}
84+
{{ prop_template.transform_multipart(property, "self." + property.python_name, property.python_name) }}
85+
{% elif prop_template.transform %}
86+
{{ prop_template.transform(property=property, source="self." + property.python_name, destination=property.python_name) }}
8787
{% else %}
8888
{{ property.python_name }} = self.{{ property.python_name }}
8989
{% endif %}
@@ -97,14 +97,12 @@ field_dict: Dict[str, Any] = {}
9797
{% else %}
9898
{% set prop_template = None %}
9999
{% endif %}
100-
{% if prop_template and prop_template.transform %}
100+
{% if prop_template and multipart %}
101101
for prop_name, prop in self.additional_properties.items():
102-
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", multipart=multipart, declare_type=false) | indent(4) }}
103-
{% elif multipart %}
104-
field_dict.update({
105-
key: (None, str(value).encode(), "text/plain")
106-
for key, value in self.additional_properties.items()
107-
})
102+
{{ prop_template.transform_multipart(model.additional_properties, "prop", "field_dict[prop_name]") | indent(4) }}
103+
{% elif prop_template and prop_template.transform %}
104+
for prop_name, prop in self.additional_properties.items():
105+
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", declare_type=false) | indent(4) }}
108106
{% else %}
109107
field_dict.update(self.additional_properties)
110108
{% endif %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% macro transform_multipart(property, source, destination) %}
2+
{% if not property.required %}
3+
{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain")
4+
{% else %}
5+
{{ destination }} = (None, str({{ source }}).encode(), "text/plain")
6+
{% endif %}
7+
{% endmacro %}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
{% macro transform_header(source) %}
22
"true" if {{ source }} else "false"
33
{% endmacro %}
4+
5+
{% macro transform_multipart(property, source, destination) %}
6+
{% if not property.required %}
7+
{{ destination }} = {{source}} if isinstance({{source}}, Unset) else (None, str({{source}}).encode(), "text/plain")
8+
{% else %}
9+
{{ destination }} = (None, str({{ source }}).encode(), "text/plain")
10+
{% endif %}
11+
{% endmacro %}

0 commit comments

Comments
 (0)