Skip to content

Commit c10c1f2

Browse files
kgutwindbanty
andauthored
Nullable array support fixes (#929)
Resolves #928 with two simple fixes: * Adds `get_type_string()` to `ListProperty` based on the definition in `ModelProperty`, since both arrays and objects are JSON-serialized in multipart mode. * Ensures that an empty list is used when `UNSET` is the default (initial) value when constructing a list property. --------- Co-authored-by: Dylan Anthony <[email protected]>
1 parent d9b2620 commit c10c1f2

20 files changed

+132
-127
lines changed

.changeset/fix_lists_within_unions.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
default: patch
3+
---
4+
5+
# Fix lists within unions
6+
7+
Fixes #756 and #928. Arrays within unions (which, as of 0.17 includes nullable arrays) would generate invalid code.
8+
9+
Thanks @kgutwin and @diesieben07!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: patch
3+
---
4+
5+
# Simplify type checks for non-required unions

end_to_end_tests/baseline_openapi_3.0.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1778,9 +1778,10 @@
17781778
},
17791779
"some_array": {
17801780
"title": "Some Array",
1781+
"nullable": true,
17811782
"type": "array",
17821783
"items": {
1783-
"type": "number"
1784+
"$ref": "#/components/schemas/AFormData"
17841785
}
17851786
},
17861787
"some_object": {

end_to_end_tests/baseline_openapi_3.1.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1794,9 +1794,9 @@ info:
17941794
},
17951795
"some_array": {
17961796
"title": "Some Array",
1797-
"type": "array",
1797+
"type": [ "array", "null" ],
17981798
"items": {
1799-
"type": "number"
1799+
"$ref": "#/components/schemas/AFormData"
18001800
}
18011801
},
18021802
"some_object": {

end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ def _get_kwargs(
5858
if isinstance(union_prop_with_ref, Unset):
5959
json_union_prop_with_ref = UNSET
6060
elif isinstance(union_prop_with_ref, AnEnum):
61-
json_union_prop_with_ref = UNSET
62-
if not isinstance(union_prop_with_ref, Unset):
63-
json_union_prop_with_ref = union_prop_with_ref.value
64-
61+
json_union_prop_with_ref = union_prop_with_ref.value
6562
else:
6663
json_union_prop_with_ref = union_prop_with_ref
6764
params["union_prop_with_ref"] = json_union_prop_with_ref

end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ def _get_kwargs(
3232
if isinstance(null_not_required, Unset):
3333
json_null_not_required = UNSET
3434
elif isinstance(null_not_required, datetime.datetime):
35-
json_null_not_required = UNSET
36-
if not isinstance(null_not_required, Unset):
37-
json_null_not_required = null_not_required.isoformat()
35+
json_null_not_required = null_not_required.isoformat()
3836
else:
3937
json_null_not_required = null_not_required
4038
params["null_not_required"] = json_null_not_required

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

+10-51
Original file line numberDiff line numberDiff line change
@@ -159,25 +159,17 @@ def to_dict(self) -> Dict[str, Any]:
159159
if isinstance(self.not_required_one_of_models, Unset):
160160
not_required_one_of_models = UNSET
161161
elif isinstance(self.not_required_one_of_models, FreeFormModel):
162-
not_required_one_of_models = UNSET
163-
if not isinstance(self.not_required_one_of_models, Unset):
164-
not_required_one_of_models = self.not_required_one_of_models.to_dict()
162+
not_required_one_of_models = self.not_required_one_of_models.to_dict()
165163
else:
166-
not_required_one_of_models = UNSET
167-
if not isinstance(self.not_required_one_of_models, Unset):
168-
not_required_one_of_models = self.not_required_one_of_models.to_dict()
164+
not_required_one_of_models = self.not_required_one_of_models.to_dict()
169165

170166
not_required_nullable_one_of_models: Union[Dict[str, Any], None, Unset, str]
171167
if isinstance(self.not_required_nullable_one_of_models, Unset):
172168
not_required_nullable_one_of_models = UNSET
173169
elif isinstance(self.not_required_nullable_one_of_models, FreeFormModel):
174-
not_required_nullable_one_of_models = UNSET
175-
if not isinstance(self.not_required_nullable_one_of_models, Unset):
176-
not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict()
170+
not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict()
177171
elif isinstance(self.not_required_nullable_one_of_models, ModelWithUnionProperty):
178-
not_required_nullable_one_of_models = UNSET
179-
if not isinstance(self.not_required_nullable_one_of_models, Unset):
180-
not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict()
172+
not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict()
181173
else:
182174
not_required_nullable_one_of_models = self.not_required_nullable_one_of_models
183175

@@ -189,9 +181,7 @@ def to_dict(self) -> Dict[str, Any]:
189181
if isinstance(self.not_required_nullable_model, Unset):
190182
not_required_nullable_model = UNSET
191183
elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty):
192-
not_required_nullable_model = UNSET
193-
if not isinstance(self.not_required_nullable_model, Unset):
194-
not_required_nullable_model = self.not_required_nullable_model.to_dict()
184+
not_required_nullable_model = self.not_required_nullable_model.to_dict()
195185
else:
196186
not_required_nullable_model = self.not_required_nullable_model
197187

@@ -401,24 +391,14 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M
401391
try:
402392
if not isinstance(data, dict):
403393
raise TypeError()
404-
_not_required_one_of_models_type_0 = data
405-
not_required_one_of_models_type_0: Union[Unset, FreeFormModel]
406-
if isinstance(_not_required_one_of_models_type_0, Unset):
407-
not_required_one_of_models_type_0 = UNSET
408-
else:
409-
not_required_one_of_models_type_0 = FreeFormModel.from_dict(_not_required_one_of_models_type_0)
394+
not_required_one_of_models_type_0 = FreeFormModel.from_dict(data)
410395

411396
return not_required_one_of_models_type_0
412397
except: # noqa: E722
413398
pass
414399
if not isinstance(data, dict):
415400
raise TypeError()
416-
_not_required_one_of_models_type_1 = data
417-
not_required_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
418-
if isinstance(_not_required_one_of_models_type_1, Unset):
419-
not_required_one_of_models_type_1 = UNSET
420-
else:
421-
not_required_one_of_models_type_1 = ModelWithUnionProperty.from_dict(_not_required_one_of_models_type_1)
401+
not_required_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data)
422402

423403
return not_required_one_of_models_type_1
424404

@@ -434,29 +414,15 @@ def _parse_not_required_nullable_one_of_models(
434414
try:
435415
if not isinstance(data, dict):
436416
raise TypeError()
437-
_not_required_nullable_one_of_models_type_0 = data
438-
not_required_nullable_one_of_models_type_0: Union[Unset, FreeFormModel]
439-
if isinstance(_not_required_nullable_one_of_models_type_0, Unset):
440-
not_required_nullable_one_of_models_type_0 = UNSET
441-
else:
442-
not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(
443-
_not_required_nullable_one_of_models_type_0
444-
)
417+
not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(data)
445418

446419
return not_required_nullable_one_of_models_type_0
447420
except: # noqa: E722
448421
pass
449422
try:
450423
if not isinstance(data, dict):
451424
raise TypeError()
452-
_not_required_nullable_one_of_models_type_1 = data
453-
not_required_nullable_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
454-
if isinstance(_not_required_nullable_one_of_models_type_1, Unset):
455-
not_required_nullable_one_of_models_type_1 = UNSET
456-
else:
457-
not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(
458-
_not_required_nullable_one_of_models_type_1
459-
)
425+
not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data)
460426

461427
return not_required_nullable_one_of_models_type_1
462428
except: # noqa: E722
@@ -482,14 +448,7 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro
482448
try:
483449
if not isinstance(data, dict):
484450
raise TypeError()
485-
_not_required_nullable_model_type_1 = data
486-
not_required_nullable_model_type_1: Union[Unset, ModelWithUnionProperty]
487-
if isinstance(_not_required_nullable_model_type_1, Unset):
488-
not_required_nullable_model_type_1 = UNSET
489-
else:
490-
not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(
491-
_not_required_nullable_model_type_1
492-
)
451+
not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data)
493452

494453
return not_required_nullable_model_type_1
495454
except: # noqa: E722

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

+46-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from ..types import UNSET, File, FileJsonType, Unset
1212

1313
if TYPE_CHECKING:
14+
from ..models.a_form_data import AFormData
1415
from ..models.body_upload_file_tests_upload_post_additional_property import (
1516
BodyUploadFileTestsUploadPostAdditionalProperty,
1617
)
@@ -38,7 +39,7 @@ class BodyUploadFileTestsUploadPost:
3839
a_datetime (Union[Unset, datetime.datetime]):
3940
a_date (Union[Unset, datetime.date]):
4041
some_number (Union[Unset, float]):
41-
some_array (Union[Unset, List[float]]):
42+
some_array (Union[List['AFormData'], None, Unset]):
4243
some_optional_object (Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]):
4344
some_enum (Union[Unset, DifferentEnum]): An enumeration.
4445
"""
@@ -51,7 +52,7 @@ class BodyUploadFileTestsUploadPost:
5152
a_datetime: Union[Unset, datetime.datetime] = UNSET
5253
a_date: Union[Unset, datetime.date] = UNSET
5354
some_number: Union[Unset, float] = UNSET
54-
some_array: Union[Unset, List[float]] = UNSET
55+
some_array: Union[List["AFormData"], None, Unset] = UNSET
5556
some_optional_object: Union[Unset, "BodyUploadFileTestsUploadPostSomeOptionalObject"] = UNSET
5657
some_enum: Union[Unset, DifferentEnum] = UNSET
5758
additional_properties: Dict[str, "BodyUploadFileTestsUploadPostAdditionalProperty"] = _attrs_field(
@@ -89,8 +90,16 @@ def to_dict(self) -> Dict[str, Any]:
8990

9091
some_number = self.some_number
9192

92-
some_array: Union[Unset, List[float]] = UNSET
93-
if not isinstance(self.some_array, Unset):
93+
some_array: Union[List[Dict[str, Any]], None, Unset]
94+
if isinstance(self.some_array, Unset):
95+
some_array = UNSET
96+
elif isinstance(self.some_array, list):
97+
some_array = []
98+
for some_array_type_0_item_data in self.some_array:
99+
some_array_type_0_item = some_array_type_0_item_data.to_dict()
100+
some_array.append(some_array_type_0_item)
101+
102+
else:
94103
some_array = self.some_array
95104

96105
some_optional_object: Union[Unset, Dict[str, Any]] = UNSET
@@ -165,11 +174,19 @@ def to_multipart(self) -> Dict[str, Any]:
165174
else (None, str(self.some_number).encode(), "text/plain")
166175
)
167176

168-
some_array: Union[Unset, Tuple[None, bytes, str]] = UNSET
169-
if not isinstance(self.some_array, Unset):
170-
_temp_some_array = self.some_array
177+
some_array: Union[None, Tuple[None, bytes, str], Unset]
178+
if isinstance(self.some_array, Unset):
179+
some_array = UNSET
180+
elif isinstance(self.some_array, list):
181+
_temp_some_array = []
182+
for some_array_type_0_item_data in self.some_array:
183+
some_array_type_0_item = some_array_type_0_item_data.to_dict()
184+
_temp_some_array.append(some_array_type_0_item)
171185
some_array = (None, json.dumps(_temp_some_array).encode(), "application/json")
172186

187+
else:
188+
some_array = self.some_array
189+
173190
some_optional_object: Union[Unset, Tuple[None, bytes, str]] = UNSET
174191
if not isinstance(self.some_optional_object, Unset):
175192
some_optional_object = (None, json.dumps(self.some_optional_object.to_dict()).encode(), "application/json")
@@ -209,6 +226,7 @@ def to_multipart(self) -> Dict[str, Any]:
209226

210227
@classmethod
211228
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
229+
from ..models.a_form_data import AFormData
212230
from ..models.body_upload_file_tests_upload_post_additional_property import (
213231
BodyUploadFileTestsUploadPostAdditionalProperty,
214232
)
@@ -265,7 +283,27 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa
265283

266284
some_number = d.pop("some_number", UNSET)
267285

268-
some_array = cast(List[float], d.pop("some_array", UNSET))
286+
def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]:
287+
if data is None:
288+
return data
289+
if isinstance(data, Unset):
290+
return data
291+
try:
292+
if not isinstance(data, list):
293+
raise TypeError()
294+
some_array_type_0 = []
295+
_some_array_type_0 = data
296+
for some_array_type_0_item_data in _some_array_type_0:
297+
some_array_type_0_item = AFormData.from_dict(some_array_type_0_item_data)
298+
299+
some_array_type_0.append(some_array_type_0_item)
300+
301+
return some_array_type_0
302+
except: # noqa: E722
303+
pass
304+
return cast(Union[List["AFormData"], None, Unset], data)
305+
306+
some_array = _parse_some_array(d.pop("some_array", UNSET))
269307

270308
_some_optional_object = d.pop("some_optional_object", UNSET)
271309
some_optional_object: Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]

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

+4-19
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,9 @@ def to_dict(self) -> Dict[str, Any]:
2323
if isinstance(self.a_property, Unset):
2424
a_property = UNSET
2525
elif isinstance(self.a_property, AnEnum):
26-
a_property = UNSET
27-
if not isinstance(self.a_property, Unset):
28-
a_property = self.a_property.value
29-
26+
a_property = self.a_property.value
3027
else:
31-
a_property = UNSET
32-
if not isinstance(self.a_property, Unset):
33-
a_property = self.a_property.value
28+
a_property = self.a_property.value
3429

3530
field_dict: Dict[str, Any] = {}
3631
field_dict.update({})
@@ -49,24 +44,14 @@ def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]:
4944
try:
5045
if not isinstance(data, str):
5146
raise TypeError()
52-
_a_property_type_0 = data
53-
a_property_type_0: Union[Unset, AnEnum]
54-
if isinstance(_a_property_type_0, Unset):
55-
a_property_type_0 = UNSET
56-
else:
57-
a_property_type_0 = AnEnum(_a_property_type_0)
47+
a_property_type_0 = AnEnum(data)
5848

5949
return a_property_type_0
6050
except: # noqa: E722
6151
pass
6252
if not isinstance(data, int):
6353
raise TypeError()
64-
_a_property_type_1 = data
65-
a_property_type_1: Union[Unset, AnIntEnum]
66-
if isinstance(_a_property_type_1, Unset):
67-
a_property_type_1 = UNSET
68-
else:
69-
a_property_type_1 = AnIntEnum(_a_property_type_1)
54+
a_property_type_1 = AnIntEnum(data)
7055

7156
return a_property_type_1
7257

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

+4-18
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,9 @@ def to_dict(self) -> Dict[str, Any]:
2828
if isinstance(self.fruit, Unset):
2929
fruit = UNSET
3030
elif isinstance(self.fruit, ModelWithUnionPropertyInlinedFruitType0):
31-
fruit = UNSET
32-
if not isinstance(self.fruit, Unset):
33-
fruit = self.fruit.to_dict()
31+
fruit = self.fruit.to_dict()
3432
else:
35-
fruit = UNSET
36-
if not isinstance(self.fruit, Unset):
37-
fruit = self.fruit.to_dict()
33+
fruit = self.fruit.to_dict()
3834

3935
field_dict: Dict[str, Any] = {}
4036
field_dict.update({})
@@ -58,24 +54,14 @@ def _parse_fruit(
5854
try:
5955
if not isinstance(data, dict):
6056
raise TypeError()
61-
_fruit_type_0 = data
62-
fruit_type_0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0]
63-
if isinstance(_fruit_type_0, Unset):
64-
fruit_type_0 = UNSET
65-
else:
66-
fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(_fruit_type_0)
57+
fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(data)
6758

6859
return fruit_type_0
6960
except: # noqa: E722
7061
pass
7162
if not isinstance(data, dict):
7263
raise TypeError()
73-
_fruit_type_1 = data
74-
fruit_type_1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1]
75-
if isinstance(_fruit_type_1, Unset):
76-
fruit_type_1 = UNSET
77-
else:
78-
fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(_fruit_type_1)
64+
fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(data)
7965

8066
return fruit_type_1
8167

0 commit comments

Comments
 (0)