Skip to content

Commit 3df7be3

Browse files
authored
Add additional property support by subscripting model (#252)
* Add additional_properties field and related helper methods to generated Models. * Support additionalProperties in OpenAPI document schemas, and freeform (no schema) objects.
1 parent dee661a commit 3df7be3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1077
-140
lines changed

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
def _parse_response(*, response: httpx.Response) -> Optional[Union[List[AModel], HTTPValidationError]]:
1818
if response.status_code == 200:
1919
response_200 = []
20-
for response_200_item_data in response.json():
20+
_response_200 = response.json()
21+
for response_200_item_data in _response_200:
2122
response_200_item = AModel.from_dict(response_200_item_data)
2223

2324
response_200.append(response_200_item)

end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@
55
from .an_int_enum import AnIntEnum
66
from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost
77
from .different_enum import DifferentEnum
8+
from .free_form_model import FreeFormModel
89
from .http_validation_error import HTTPValidationError
10+
from .model_with_additional_properties_inlined import ModelWithAdditionalPropertiesInlined
11+
from .model_with_additional_properties_inlined_additional_property import (
12+
ModelWithAdditionalPropertiesInlinedAdditionalProperty,
13+
)
14+
from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed
15+
from .model_with_primitive_additional_properties import ModelWithPrimitiveAdditionalProperties
16+
from .model_with_primitive_additional_properties_a_date_holder import ModelWithPrimitiveAdditionalPropertiesADateHolder
917
from .model_with_union_property import ModelWithUnionProperty
1018
from .test_inline_objects_json_body import TestInlineObjectsJsonBody
1119
from .test_inline_objects_response_200 import TestInlineObjectsResponse_200

end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,16 @@ def to_dict(self) -> Dict[str, Any]:
5252
not_required_nullable = self.not_required_nullable
5353
not_required_not_nullable = self.not_required_not_nullable
5454

55-
field_dict = {
56-
"an_enum_value": an_enum_value,
57-
"aCamelDateTime": a_camel_date_time,
58-
"a_date": a_date,
59-
"required_not_nullable": required_not_nullable,
60-
"required_nullable": required_nullable,
61-
}
55+
field_dict: Dict[str, Any] = {}
56+
field_dict.update(
57+
{
58+
"an_enum_value": an_enum_value,
59+
"aCamelDateTime": a_camel_date_time,
60+
"a_date": a_date,
61+
"required_not_nullable": required_not_nullable,
62+
"required_nullable": required_nullable,
63+
}
64+
)
6265
if nested_list_of_enums is not UNSET:
6366
field_dict["nested_list_of_enums"] = nested_list_of_enums
6467
if attr_1_leading_digit is not UNSET:
@@ -71,8 +74,9 @@ def to_dict(self) -> Dict[str, Any]:
7174
return field_dict
7275

7376
@staticmethod
74-
def from_dict(d: Dict[str, Any]) -> "AModel":
75-
an_enum_value = AnEnum(d["an_enum_value"])
77+
def from_dict(src_dict: Dict[str, Any]) -> "AModel":
78+
d = src_dict.copy()
79+
an_enum_value = AnEnum(d.pop("an_enum_value"))
7680

7781
def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.date]:
7882
data = None if isinstance(data, Unset) else data
@@ -87,31 +91,33 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
8791

8892
return a_camel_date_time
8993

90-
a_camel_date_time = _parse_a_camel_date_time(d["aCamelDateTime"])
94+
a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime"))
9195

92-
a_date = isoparse(d["a_date"]).date()
96+
a_date = isoparse(d.pop("a_date")).date()
9397

94-
required_not_nullable = d["required_not_nullable"]
98+
required_not_nullable = d.pop("required_not_nullable")
9599

96100
nested_list_of_enums = []
97-
for nested_list_of_enums_item_data in d.get("nested_list_of_enums", UNSET) or []:
101+
_nested_list_of_enums = d.pop("nested_list_of_enums", UNSET)
102+
for nested_list_of_enums_item_data in _nested_list_of_enums or []:
98103
nested_list_of_enums_item = []
99-
for nested_list_of_enums_item_item_data in nested_list_of_enums_item_data:
104+
_nested_list_of_enums_item = nested_list_of_enums_item_data
105+
for nested_list_of_enums_item_item_data in _nested_list_of_enums_item:
100106
nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data)
101107

102108
nested_list_of_enums_item.append(nested_list_of_enums_item_item)
103109

104110
nested_list_of_enums.append(nested_list_of_enums_item)
105111

106-
attr_1_leading_digit = d.get("1_leading_digit", UNSET)
112+
attr_1_leading_digit = d.pop("1_leading_digit", UNSET)
107113

108-
required_nullable = d["required_nullable"]
114+
required_nullable = d.pop("required_nullable")
109115

110-
not_required_nullable = d.get("not_required_nullable", UNSET)
116+
not_required_nullable = d.pop("not_required_nullable", UNSET)
111117

112-
not_required_not_nullable = d.get("not_required_not_nullable", UNSET)
118+
not_required_not_nullable = d.pop("not_required_not_nullable", UNSET)
113119

114-
return AModel(
120+
a_model = AModel(
115121
an_enum_value=an_enum_value,
116122
a_camel_date_time=a_camel_date_time,
117123
a_date=a_date,
@@ -122,3 +128,5 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
122128
not_required_nullable=not_required_nullable,
123129
not_required_not_nullable=not_required_not_nullable,
124130
)
131+
132+
return a_model

end_to_end_tests/golden-record-custom/custom_e2e/models/body_upload_file_tests_upload_post.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,22 @@ class BodyUploadFileTestsUploadPost:
1515
def to_dict(self) -> Dict[str, Any]:
1616
some_file = self.some_file.to_tuple()
1717

18-
field_dict = {
19-
"some_file": some_file,
20-
}
18+
field_dict: Dict[str, Any] = {}
19+
field_dict.update(
20+
{
21+
"some_file": some_file,
22+
}
23+
)
2124

2225
return field_dict
2326

2427
@staticmethod
25-
def from_dict(d: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost":
26-
some_file = File(payload=BytesIO(d["some_file"]))
28+
def from_dict(src_dict: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost":
29+
d = src_dict.copy()
30+
some_file = File(payload=BytesIO(d.pop("some_file")))
2731

28-
return BodyUploadFileTestsUploadPost(
32+
body_upload_file_tests_upload_post = BodyUploadFileTestsUploadPost(
2933
some_file=some_file,
3034
)
35+
36+
return body_upload_file_tests_upload_post
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from typing import Any, Dict, List
2+
3+
import attr
4+
5+
6+
@attr.s(auto_attribs=True)
7+
class FreeFormModel:
8+
""" """
9+
10+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
11+
12+
def to_dict(self) -> Dict[str, Any]:
13+
14+
field_dict: Dict[str, Any] = {}
15+
field_dict.update(self.additional_properties)
16+
field_dict.update({})
17+
18+
return field_dict
19+
20+
@staticmethod
21+
def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel":
22+
d = src_dict.copy()
23+
free_form_model = FreeFormModel()
24+
25+
free_form_model.additional_properties = d
26+
return free_form_model
27+
28+
@property
29+
def additional_keys(self) -> List[str]:
30+
return list(self.additional_properties.keys())
31+
32+
def __getitem__(self, key: str) -> Any:
33+
return self.additional_properties[key]
34+
35+
def __setitem__(self, key: str, value: Any) -> None:
36+
self.additional_properties[key] = value
37+
38+
def __delitem__(self, key: str) -> None:
39+
del self.additional_properties[key]
40+
41+
def __contains__(self, key: str) -> bool:
42+
return key in self.additional_properties

end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,25 @@ def to_dict(self) -> Dict[str, Any]:
2121

2222
detail.append(detail_item)
2323

24-
field_dict = {}
24+
field_dict: Dict[str, Any] = {}
25+
field_dict.update({})
2526
if detail is not UNSET:
2627
field_dict["detail"] = detail
2728

2829
return field_dict
2930

3031
@staticmethod
31-
def from_dict(d: Dict[str, Any]) -> "HTTPValidationError":
32+
def from_dict(src_dict: Dict[str, Any]) -> "HTTPValidationError":
33+
d = src_dict.copy()
3234
detail = []
33-
for detail_item_data in d.get("detail", UNSET) or []:
35+
_detail = d.pop("detail", UNSET)
36+
for detail_item_data in _detail or []:
3437
detail_item = ValidationError.from_dict(detail_item_data)
3538

3639
detail.append(detail_item)
3740

38-
return HTTPValidationError(
41+
http_validation_error = HTTPValidationError(
3942
detail=detail,
4043
)
44+
45+
return http_validation_error
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from typing import Any, Dict, List, Union
2+
3+
import attr
4+
5+
from ..models.model_with_additional_properties_inlined_additional_property import (
6+
ModelWithAdditionalPropertiesInlinedAdditionalProperty,
7+
)
8+
from ..types import UNSET, Unset
9+
10+
11+
@attr.s(auto_attribs=True)
12+
class ModelWithAdditionalPropertiesInlined:
13+
""" """
14+
15+
a_number: Union[Unset, float] = UNSET
16+
additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = attr.ib(
17+
init=False, factory=dict
18+
)
19+
20+
def to_dict(self) -> Dict[str, Any]:
21+
a_number = self.a_number
22+
23+
field_dict: Dict[str, Any] = {}
24+
for prop_name, prop in self.additional_properties.items():
25+
field_dict[prop_name] = prop.to_dict()
26+
27+
field_dict.update({})
28+
if a_number is not UNSET:
29+
field_dict["a_number"] = a_number
30+
31+
return field_dict
32+
33+
@staticmethod
34+
def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined":
35+
d = src_dict.copy()
36+
a_number = d.pop("a_number", UNSET)
37+
38+
model_with_additional_properties_inlined = ModelWithAdditionalPropertiesInlined(
39+
a_number=a_number,
40+
)
41+
42+
additional_properties = {}
43+
for prop_name, prop_dict in d.items():
44+
additional_property = ModelWithAdditionalPropertiesInlinedAdditionalProperty.from_dict(prop_dict)
45+
46+
additional_properties[prop_name] = additional_property
47+
48+
model_with_additional_properties_inlined.additional_properties = additional_properties
49+
return model_with_additional_properties_inlined
50+
51+
@property
52+
def additional_keys(self) -> List[str]:
53+
return list(self.additional_properties.keys())
54+
55+
def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty:
56+
return self.additional_properties[key]
57+
58+
def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None:
59+
self.additional_properties[key] = value
60+
61+
def __delitem__(self, key: str) -> None:
62+
del self.additional_properties[key]
63+
64+
def __contains__(self, key: str) -> bool:
65+
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, Union
2+
3+
import attr
4+
5+
from ..types import UNSET, Unset
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class ModelWithAdditionalPropertiesInlinedAdditionalProperty:
10+
""" """
11+
12+
extra_props_prop: Union[Unset, str] = UNSET
13+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
14+
15+
def to_dict(self) -> Dict[str, Any]:
16+
extra_props_prop = self.extra_props_prop
17+
18+
field_dict: Dict[str, Any] = {}
19+
field_dict.update(self.additional_properties)
20+
field_dict.update({})
21+
if extra_props_prop is not UNSET:
22+
field_dict["extra_props_prop"] = extra_props_prop
23+
24+
return field_dict
25+
26+
@staticmethod
27+
def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperty":
28+
d = src_dict.copy()
29+
extra_props_prop = d.pop("extra_props_prop", UNSET)
30+
31+
model_with_additional_properties_inlined_additional_property = (
32+
ModelWithAdditionalPropertiesInlinedAdditionalProperty(
33+
extra_props_prop=extra_props_prop,
34+
)
35+
)
36+
37+
model_with_additional_properties_inlined_additional_property.additional_properties = d
38+
return model_with_additional_properties_inlined_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
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import Any, Dict, List
2+
3+
import attr
4+
5+
from ..models.an_enum import AnEnum
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class ModelWithAdditionalPropertiesRefed:
10+
""" """
11+
12+
additional_properties: Dict[str, AnEnum] = attr.ib(init=False, factory=dict)
13+
14+
def to_dict(self) -> Dict[str, Any]:
15+
16+
field_dict: Dict[str, Any] = {}
17+
for prop_name, prop in self.additional_properties.items():
18+
field_dict[prop_name] = prop.value
19+
20+
field_dict.update({})
21+
22+
return field_dict
23+
24+
@staticmethod
25+
def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed":
26+
d = src_dict.copy()
27+
model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed()
28+
29+
additional_properties = {}
30+
for prop_name, prop_dict in d.items():
31+
additional_property = AnEnum(prop_dict)
32+
33+
additional_properties[prop_name] = additional_property
34+
35+
model_with_additional_properties_refed.additional_properties = additional_properties
36+
return model_with_additional_properties_refed
37+
38+
@property
39+
def additional_keys(self) -> List[str]:
40+
return list(self.additional_properties.keys())
41+
42+
def __getitem__(self, key: str) -> AnEnum:
43+
return self.additional_properties[key]
44+
45+
def __setitem__(self, key: str, value: AnEnum) -> None:
46+
self.additional_properties[key] = value
47+
48+
def __delitem__(self, key: str) -> None:
49+
del self.additional_properties[key]
50+
51+
def __contains__(self, key: str) -> bool:
52+
return key in self.additional_properties

0 commit comments

Comments
 (0)