diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py index 2821664fb..bc3909126 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py @@ -17,7 +17,8 @@ def _parse_response(*, response: httpx.Response) -> Optional[Union[List[AModel], HTTPValidationError]]: if response.status_code == 200: response_200 = [] - for response_200_item_data in response.json(): + _response_200 = response.json() + for response_200_item_data in _response_200: response_200_item = AModel.from_dict(response_200_item_data) response_200.append(response_200_item) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py index a01ec4327..475172136 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py @@ -5,7 +5,15 @@ from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost from .different_enum import DifferentEnum +from .free_form_model import FreeFormModel from .http_validation_error import HTTPValidationError +from .model_with_additional_properties_inlined import ModelWithAdditionalPropertiesInlined +from .model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, +) +from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed +from .model_with_primitive_additional_properties import ModelWithPrimitiveAdditionalProperties +from .model_with_primitive_additional_properties_a_date_holder import ModelWithPrimitiveAdditionalPropertiesADateHolder from .model_with_union_property import ModelWithUnionProperty from .test_inline_objects_json_body import TestInlineObjectsJsonBody from .test_inline_objects_response_200 import TestInlineObjectsResponse_200 diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py index f523e36f6..fc857027a 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py @@ -52,13 +52,16 @@ def to_dict(self) -> Dict[str, Any]: not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable - field_dict = { - "an_enum_value": an_enum_value, - "aCamelDateTime": a_camel_date_time, - "a_date": a_date, - "required_not_nullable": required_not_nullable, - "required_nullable": required_nullable, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "an_enum_value": an_enum_value, + "aCamelDateTime": a_camel_date_time, + "a_date": a_date, + "required_not_nullable": required_not_nullable, + "required_nullable": required_nullable, + } + ) if nested_list_of_enums is not UNSET: field_dict["nested_list_of_enums"] = nested_list_of_enums if attr_1_leading_digit is not UNSET: @@ -71,8 +74,9 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "AModel": - an_enum_value = AnEnum(d["an_enum_value"]) + def from_dict(src_dict: Dict[str, Any]) -> "AModel": + d = src_dict.copy() + an_enum_value = AnEnum(d.pop("an_enum_value")) def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.date]: 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 return a_camel_date_time - a_camel_date_time = _parse_a_camel_date_time(d["aCamelDateTime"]) + a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) - a_date = isoparse(d["a_date"]).date() + a_date = isoparse(d.pop("a_date")).date() - required_not_nullable = d["required_not_nullable"] + required_not_nullable = d.pop("required_not_nullable") nested_list_of_enums = [] - for nested_list_of_enums_item_data in d.get("nested_list_of_enums", UNSET) or []: + _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) + for nested_list_of_enums_item_data in _nested_list_of_enums or []: nested_list_of_enums_item = [] - for nested_list_of_enums_item_item_data in nested_list_of_enums_item_data: + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) nested_list_of_enums_item.append(nested_list_of_enums_item_item) nested_list_of_enums.append(nested_list_of_enums_item) - attr_1_leading_digit = d.get("1_leading_digit", UNSET) + attr_1_leading_digit = d.pop("1_leading_digit", UNSET) - required_nullable = d["required_nullable"] + required_nullable = d.pop("required_nullable") - not_required_nullable = d.get("not_required_nullable", UNSET) + not_required_nullable = d.pop("not_required_nullable", UNSET) - not_required_not_nullable = d.get("not_required_not_nullable", UNSET) + not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - return AModel( + a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, @@ -122,3 +128,5 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, ) + + return a_model diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/body_upload_file_tests_upload_post.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/body_upload_file_tests_upload_post.py index 93f2dd68f..657411d54 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/body_upload_file_tests_upload_post.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/body_upload_file_tests_upload_post.py @@ -15,16 +15,22 @@ class BodyUploadFileTestsUploadPost: def to_dict(self) -> Dict[str, Any]: some_file = self.some_file.to_tuple() - field_dict = { - "some_file": some_file, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "some_file": some_file, + } + ) return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost": - some_file = File(payload=BytesIO(d["some_file"])) + def from_dict(src_dict: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost": + d = src_dict.copy() + some_file = File(payload=BytesIO(d.pop("some_file"))) - return BodyUploadFileTestsUploadPost( + body_upload_file_tests_upload_post = BodyUploadFileTestsUploadPost( some_file=some_file, ) + + return body_upload_file_tests_upload_post diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/free_form_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/free_form_model.py new file mode 100644 index 000000000..0e1b93899 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/free_form_model.py @@ -0,0 +1,42 @@ +from typing import Any, Dict, List + +import attr + + +@attr.s(auto_attribs=True) +class FreeFormModel: + """ """ + + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": + d = src_dict.copy() + free_form_model = FreeFormModel() + + free_form_model.additional_properties = d + return free_form_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py index 9d29faa4d..1b25a6d98 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py @@ -21,20 +21,25 @@ def to_dict(self) -> Dict[str, Any]: detail.append(detail_item) - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if detail is not UNSET: field_dict["detail"] = detail return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "HTTPValidationError": + def from_dict(src_dict: Dict[str, Any]) -> "HTTPValidationError": + d = src_dict.copy() detail = [] - for detail_item_data in d.get("detail", UNSET) or []: + _detail = d.pop("detail", UNSET) + for detail_item_data in _detail or []: detail_item = ValidationError.from_dict(detail_item_data) detail.append(detail_item) - return HTTPValidationError( + http_validation_error = HTTPValidationError( detail=detail, ) + + return http_validation_error diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py new file mode 100644 index 000000000..30acb7957 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py @@ -0,0 +1,65 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlined: + """ """ + + a_number: Union[Unset, float] = UNSET + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = attr.ib( + init=False, factory=dict + ) + + def to_dict(self) -> Dict[str, Any]: + a_number = self.a_number + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.to_dict() + + field_dict.update({}) + if a_number is not UNSET: + field_dict["a_number"] = a_number + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined": + d = src_dict.copy() + a_number = d.pop("a_number", UNSET) + + model_with_additional_properties_inlined = ModelWithAdditionalPropertiesInlined( + a_number=a_number, + ) + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = ModelWithAdditionalPropertiesInlinedAdditionalProperty.from_dict(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_additional_properties_inlined.additional_properties = additional_properties + return model_with_additional_properties_inlined + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py new file mode 100644 index 000000000..4ce45f8ef --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlinedAdditionalProperty: + """ """ + + extra_props_prop: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + extra_props_prop = self.extra_props_prop + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if extra_props_prop is not UNSET: + field_dict["extra_props_prop"] = extra_props_prop + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperty": + d = src_dict.copy() + extra_props_prop = d.pop("extra_props_prop", UNSET) + + model_with_additional_properties_inlined_additional_property = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty( + extra_props_prop=extra_props_prop, + ) + ) + + model_with_additional_properties_inlined_additional_property.additional_properties = d + return model_with_additional_properties_inlined_additional_property + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_refed.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_refed.py new file mode 100644 index 000000000..c5eb4a7a4 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_refed.py @@ -0,0 +1,52 @@ +from typing import Any, Dict, List + +import attr + +from ..models.an_enum import AnEnum + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesRefed: + """ """ + + additional_properties: Dict[str, AnEnum] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.value + + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": + d = src_dict.copy() + model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = AnEnum(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_additional_properties_refed.additional_properties = additional_properties + return model_with_additional_properties_refed + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> AnEnum: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: AnEnum) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py new file mode 100644 index 000000000..ae987b384 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py @@ -0,0 +1,62 @@ +from typing import Any, Dict, List, Union, cast + +import attr + +from ..models.model_with_primitive_additional_properties_a_date_holder import ( + ModelWithPrimitiveAdditionalPropertiesADateHolder, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithPrimitiveAdditionalProperties: + """ """ + + a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_date_holder: Union[Unset, Dict[str, Any]] = UNSET + if not isinstance(self.a_date_holder, Unset): + a_date_holder = self.a_date_holder.to_dict() + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_date_holder is not UNSET: + field_dict["a_date_holder"] = a_date_holder + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperties": + d = src_dict.copy() + a_date_holder = UNSET + _a_date_holder = d.pop("a_date_holder", UNSET) + if _a_date_holder is not None and not isinstance(a_date_holder, Unset): + a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( + cast(Dict[str, Any], _a_date_holder) + ) + + model_with_primitive_additional_properties = ModelWithPrimitiveAdditionalProperties( + a_date_holder=a_date_holder, + ) + + model_with_primitive_additional_properties.additional_properties = d + return model_with_primitive_additional_properties + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> str: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: str) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties_a_date_holder.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties_a_date_holder.py new file mode 100644 index 000000000..394c68a12 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties_a_date_holder.py @@ -0,0 +1,52 @@ +import datetime +from typing import Any, Dict, List + +import attr +from dateutil.parser import isoparse + + +@attr.s(auto_attribs=True) +class ModelWithPrimitiveAdditionalPropertiesADateHolder: + """ """ + + additional_properties: Dict[str, datetime.datetime] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.isoformat() + + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalPropertiesADateHolder": + d = src_dict.copy() + model_with_primitive_additional_properties_a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder() + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = isoparse(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties + return model_with_primitive_additional_properties_a_date_holder + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> datetime.datetime: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: datetime.datetime) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py index 2ca963e01..cbecc7c90 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py @@ -27,33 +27,40 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.a_property, Unset): a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "ModelWithUnionProperty": + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithUnionProperty": + d = src_dict.copy() + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: data = None if isinstance(data, Unset) else data a_property: Union[Unset, AnEnum, AnIntEnum] try: a_property = UNSET - if data is not None: - a_property = AnEnum(data) + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) return a_property except: # noqa: E722 pass a_property = UNSET - if data is not None: - a_property = AnIntEnum(data) + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) return a_property - a_property = _parse_a_property(d.get("a_property", UNSET)) + a_property = _parse_a_property(d.pop("a_property", UNSET)) - return ModelWithUnionProperty( + model_with_union_property = ModelWithUnionProperty( a_property=a_property, ) + + return model_with_union_property diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_json_body.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_json_body.py index d40f4f057..e607209ca 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_json_body.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_json_body.py @@ -14,16 +14,20 @@ class TestInlineObjectsJsonBody: def to_dict(self) -> Dict[str, Any]: a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "TestInlineObjectsJsonBody": - a_property = d.get("a_property", UNSET) + def from_dict(src_dict: Dict[str, Any]) -> "TestInlineObjectsJsonBody": + d = src_dict.copy() + a_property = d.pop("a_property", UNSET) - return TestInlineObjectsJsonBody( + test_inline_objects_json_body = TestInlineObjectsJsonBody( a_property=a_property, ) + + return test_inline_objects_json_body diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_response_200.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_response_200.py index 824dd3b5e..79daf6731 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_response_200.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/test_inline_objects_response_200.py @@ -14,16 +14,20 @@ class TestInlineObjectsResponse_200: def to_dict(self) -> Dict[str, Any]: a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "TestInlineObjectsResponse_200": - a_property = d.get("a_property", UNSET) + def from_dict(src_dict: Dict[str, Any]) -> "TestInlineObjectsResponse_200": + d = src_dict.copy() + a_property = d.pop("a_property", UNSET) - return TestInlineObjectsResponse_200( + test_inline_objects_response_200 = TestInlineObjectsResponse_200( a_property=a_property, ) + + return test_inline_objects_response_200 diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/validation_error.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/validation_error.py index cadbc0d5b..2cd2b8959 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/validation_error.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/validation_error.py @@ -17,24 +17,30 @@ def to_dict(self) -> Dict[str, Any]: msg = self.msg type = self.type - field_dict = { - "loc": loc, - "msg": msg, - "type": type, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "loc": loc, + "msg": msg, + "type": type, + } + ) return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "ValidationError": - loc = cast(List[str], d["loc"]) + def from_dict(src_dict: Dict[str, Any]) -> "ValidationError": + d = src_dict.copy() + loc = cast(List[str], d.pop("loc")) - msg = d["msg"] + msg = d.pop("msg") - type = d["type"] + type = d.pop("type") - return ValidationError( + validation_error = ValidationError( loc=loc, msg=msg, type=type, ) + + return validation_error diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py index 04f46d685..f8d0062a7 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py @@ -49,7 +49,8 @@ def _get_kwargs( def _parse_response(*, response: httpx.Response) -> Optional[Union[List[AModel], HTTPValidationError]]: if response.status_code == 200: response_200 = [] - for response_200_item_data in response.json(): + _response_200 = response.json() + for response_200_item_data in _response_200: response_200_item = AModel.from_dict(response_200_item_data) response_200.append(response_200_item) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index a01ec4327..475172136 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -5,7 +5,15 @@ from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost from .different_enum import DifferentEnum +from .free_form_model import FreeFormModel from .http_validation_error import HTTPValidationError +from .model_with_additional_properties_inlined import ModelWithAdditionalPropertiesInlined +from .model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, +) +from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed +from .model_with_primitive_additional_properties import ModelWithPrimitiveAdditionalProperties +from .model_with_primitive_additional_properties_a_date_holder import ModelWithPrimitiveAdditionalPropertiesADateHolder from .model_with_union_property import ModelWithUnionProperty from .test_inline_objects_json_body import TestInlineObjectsJsonBody from .test_inline_objects_response_200 import TestInlineObjectsResponse_200 diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index f523e36f6..fc857027a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py @@ -52,13 +52,16 @@ def to_dict(self) -> Dict[str, Any]: not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable - field_dict = { - "an_enum_value": an_enum_value, - "aCamelDateTime": a_camel_date_time, - "a_date": a_date, - "required_not_nullable": required_not_nullable, - "required_nullable": required_nullable, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "an_enum_value": an_enum_value, + "aCamelDateTime": a_camel_date_time, + "a_date": a_date, + "required_not_nullable": required_not_nullable, + "required_nullable": required_nullable, + } + ) if nested_list_of_enums is not UNSET: field_dict["nested_list_of_enums"] = nested_list_of_enums if attr_1_leading_digit is not UNSET: @@ -71,8 +74,9 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "AModel": - an_enum_value = AnEnum(d["an_enum_value"]) + def from_dict(src_dict: Dict[str, Any]) -> "AModel": + d = src_dict.copy() + an_enum_value = AnEnum(d.pop("an_enum_value")) def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.date]: 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 return a_camel_date_time - a_camel_date_time = _parse_a_camel_date_time(d["aCamelDateTime"]) + a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) - a_date = isoparse(d["a_date"]).date() + a_date = isoparse(d.pop("a_date")).date() - required_not_nullable = d["required_not_nullable"] + required_not_nullable = d.pop("required_not_nullable") nested_list_of_enums = [] - for nested_list_of_enums_item_data in d.get("nested_list_of_enums", UNSET) or []: + _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) + for nested_list_of_enums_item_data in _nested_list_of_enums or []: nested_list_of_enums_item = [] - for nested_list_of_enums_item_item_data in nested_list_of_enums_item_data: + _nested_list_of_enums_item = nested_list_of_enums_item_data + for nested_list_of_enums_item_item_data in _nested_list_of_enums_item: nested_list_of_enums_item_item = DifferentEnum(nested_list_of_enums_item_item_data) nested_list_of_enums_item.append(nested_list_of_enums_item_item) nested_list_of_enums.append(nested_list_of_enums_item) - attr_1_leading_digit = d.get("1_leading_digit", UNSET) + attr_1_leading_digit = d.pop("1_leading_digit", UNSET) - required_nullable = d["required_nullable"] + required_nullable = d.pop("required_nullable") - not_required_nullable = d.get("not_required_nullable", UNSET) + not_required_nullable = d.pop("not_required_nullable", UNSET) - not_required_not_nullable = d.get("not_required_not_nullable", UNSET) + not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - return AModel( + a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, @@ -122,3 +128,5 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, ) + + return a_model diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py index 93f2dd68f..657411d54 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/body_upload_file_tests_upload_post.py @@ -15,16 +15,22 @@ class BodyUploadFileTestsUploadPost: def to_dict(self) -> Dict[str, Any]: some_file = self.some_file.to_tuple() - field_dict = { - "some_file": some_file, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "some_file": some_file, + } + ) return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost": - some_file = File(payload=BytesIO(d["some_file"])) + def from_dict(src_dict: Dict[str, Any]) -> "BodyUploadFileTestsUploadPost": + d = src_dict.copy() + some_file = File(payload=BytesIO(d.pop("some_file"))) - return BodyUploadFileTestsUploadPost( + body_upload_file_tests_upload_post = BodyUploadFileTestsUploadPost( some_file=some_file, ) + + return body_upload_file_tests_upload_post diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py new file mode 100644 index 000000000..0e1b93899 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py @@ -0,0 +1,42 @@ +from typing import Any, Dict, List + +import attr + + +@attr.s(auto_attribs=True) +class FreeFormModel: + """ """ + + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": + d = src_dict.copy() + free_form_model = FreeFormModel() + + free_form_model.additional_properties = d + return free_form_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py b/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py index 9d29faa4d..1b25a6d98 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py @@ -21,20 +21,25 @@ def to_dict(self) -> Dict[str, Any]: detail.append(detail_item) - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if detail is not UNSET: field_dict["detail"] = detail return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "HTTPValidationError": + def from_dict(src_dict: Dict[str, Any]) -> "HTTPValidationError": + d = src_dict.copy() detail = [] - for detail_item_data in d.get("detail", UNSET) or []: + _detail = d.pop("detail", UNSET) + for detail_item_data in _detail or []: detail_item = ValidationError.from_dict(detail_item_data) detail.append(detail_item) - return HTTPValidationError( + http_validation_error = HTTPValidationError( detail=detail, ) + + return http_validation_error diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py new file mode 100644 index 000000000..30acb7957 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py @@ -0,0 +1,65 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlined: + """ """ + + a_number: Union[Unset, float] = UNSET + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = attr.ib( + init=False, factory=dict + ) + + def to_dict(self) -> Dict[str, Any]: + a_number = self.a_number + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.to_dict() + + field_dict.update({}) + if a_number is not UNSET: + field_dict["a_number"] = a_number + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined": + d = src_dict.copy() + a_number = d.pop("a_number", UNSET) + + model_with_additional_properties_inlined = ModelWithAdditionalPropertiesInlined( + a_number=a_number, + ) + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = ModelWithAdditionalPropertiesInlinedAdditionalProperty.from_dict(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_additional_properties_inlined.additional_properties = additional_properties + return model_with_additional_properties_inlined + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py new file mode 100644 index 000000000..4ce45f8ef --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlinedAdditionalProperty: + """ """ + + extra_props_prop: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + extra_props_prop = self.extra_props_prop + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if extra_props_prop is not UNSET: + field_dict["extra_props_prop"] = extra_props_prop + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperty": + d = src_dict.copy() + extra_props_prop = d.pop("extra_props_prop", UNSET) + + model_with_additional_properties_inlined_additional_property = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty( + extra_props_prop=extra_props_prop, + ) + ) + + model_with_additional_properties_inlined_additional_property.additional_properties = d + return model_with_additional_properties_inlined_additional_property + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py new file mode 100644 index 000000000..c5eb4a7a4 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py @@ -0,0 +1,52 @@ +from typing import Any, Dict, List + +import attr + +from ..models.an_enum import AnEnum + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesRefed: + """ """ + + additional_properties: Dict[str, AnEnum] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.value + + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": + d = src_dict.copy() + model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = AnEnum(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_additional_properties_refed.additional_properties = additional_properties + return model_with_additional_properties_refed + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> AnEnum: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: AnEnum) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py new file mode 100644 index 000000000..ae987b384 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py @@ -0,0 +1,62 @@ +from typing import Any, Dict, List, Union, cast + +import attr + +from ..models.model_with_primitive_additional_properties_a_date_holder import ( + ModelWithPrimitiveAdditionalPropertiesADateHolder, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithPrimitiveAdditionalProperties: + """ """ + + a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_date_holder: Union[Unset, Dict[str, Any]] = UNSET + if not isinstance(self.a_date_holder, Unset): + a_date_holder = self.a_date_holder.to_dict() + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_date_holder is not UNSET: + field_dict["a_date_holder"] = a_date_holder + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperties": + d = src_dict.copy() + a_date_holder = UNSET + _a_date_holder = d.pop("a_date_holder", UNSET) + if _a_date_holder is not None and not isinstance(a_date_holder, Unset): + a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( + cast(Dict[str, Any], _a_date_holder) + ) + + model_with_primitive_additional_properties = ModelWithPrimitiveAdditionalProperties( + a_date_holder=a_date_holder, + ) + + model_with_primitive_additional_properties.additional_properties = d + return model_with_primitive_additional_properties + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> str: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: str) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py new file mode 100644 index 000000000..394c68a12 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py @@ -0,0 +1,52 @@ +import datetime +from typing import Any, Dict, List + +import attr +from dateutil.parser import isoparse + + +@attr.s(auto_attribs=True) +class ModelWithPrimitiveAdditionalPropertiesADateHolder: + """ """ + + additional_properties: Dict[str, datetime.datetime] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + + field_dict: Dict[str, Any] = {} + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.isoformat() + + field_dict.update({}) + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalPropertiesADateHolder": + d = src_dict.copy() + model_with_primitive_additional_properties_a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder() + + additional_properties = {} + for prop_name, prop_dict in d.items(): + additional_property = isoparse(prop_dict) + + additional_properties[prop_name] = additional_property + + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties + return model_with_primitive_additional_properties_a_date_holder + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> datetime.datetime: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: datetime.datetime) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py index 2ca963e01..cbecc7c90 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py @@ -27,33 +27,40 @@ def to_dict(self) -> Dict[str, Any]: if not isinstance(self.a_property, Unset): a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "ModelWithUnionProperty": + def from_dict(src_dict: Dict[str, Any]) -> "ModelWithUnionProperty": + d = src_dict.copy() + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: data = None if isinstance(data, Unset) else data a_property: Union[Unset, AnEnum, AnIntEnum] try: a_property = UNSET - if data is not None: - a_property = AnEnum(data) + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) return a_property except: # noqa: E722 pass a_property = UNSET - if data is not None: - a_property = AnIntEnum(data) + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) return a_property - a_property = _parse_a_property(d.get("a_property", UNSET)) + a_property = _parse_a_property(d.pop("a_property", UNSET)) - return ModelWithUnionProperty( + model_with_union_property = ModelWithUnionProperty( a_property=a_property, ) + + return model_with_union_property diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_json_body.py b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_json_body.py index d40f4f057..e607209ca 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_json_body.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_json_body.py @@ -14,16 +14,20 @@ class TestInlineObjectsJsonBody: def to_dict(self) -> Dict[str, Any]: a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "TestInlineObjectsJsonBody": - a_property = d.get("a_property", UNSET) + def from_dict(src_dict: Dict[str, Any]) -> "TestInlineObjectsJsonBody": + d = src_dict.copy() + a_property = d.pop("a_property", UNSET) - return TestInlineObjectsJsonBody( + test_inline_objects_json_body = TestInlineObjectsJsonBody( a_property=a_property, ) + + return test_inline_objects_json_body diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py index 824dd3b5e..79daf6731 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/test_inline_objects_response_200.py @@ -14,16 +14,20 @@ class TestInlineObjectsResponse_200: def to_dict(self) -> Dict[str, Any]: a_property = self.a_property - field_dict = {} + field_dict: Dict[str, Any] = {} + field_dict.update({}) if a_property is not UNSET: field_dict["a_property"] = a_property return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "TestInlineObjectsResponse_200": - a_property = d.get("a_property", UNSET) + def from_dict(src_dict: Dict[str, Any]) -> "TestInlineObjectsResponse_200": + d = src_dict.copy() + a_property = d.pop("a_property", UNSET) - return TestInlineObjectsResponse_200( + test_inline_objects_response_200 = TestInlineObjectsResponse_200( a_property=a_property, ) + + return test_inline_objects_response_200 diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py b/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py index cadbc0d5b..2cd2b8959 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/validation_error.py @@ -17,24 +17,30 @@ def to_dict(self) -> Dict[str, Any]: msg = self.msg type = self.type - field_dict = { - "loc": loc, - "msg": msg, - "type": type, - } + field_dict: Dict[str, Any] = {} + field_dict.update( + { + "loc": loc, + "msg": msg, + "type": type, + } + ) return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "ValidationError": - loc = cast(List[str], d["loc"]) + def from_dict(src_dict: Dict[str, Any]) -> "ValidationError": + d = src_dict.copy() + loc = cast(List[str], d.pop("loc")) - msg = d["msg"] + msg = d.pop("msg") - type = d["type"] + type = d.pop("type") - return ValidationError( + validation_error = ValidationError( loc=loc, msg=msg, type=type, ) + + return validation_error diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 426127f17..1cbfd9597 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -543,7 +543,8 @@ "a_property": { "type": "string" } - } + }, + "additionalProperties": false } } } @@ -559,7 +560,8 @@ "a_property": { "type": "string" } - } + }, + "additionalProperties": false } } } @@ -680,7 +682,8 @@ "nullable": false } }, - "description": "A Model for testing all the ways custom objects can be used " + "description": "A Model for testing all the ways custom objects can be used ", + "additionalProperties": false }, "AnEnum": { "title": "AnEnum", @@ -703,7 +706,8 @@ "type": "string", "format": "binary" } - } + }, + "additionalProperties": false }, "DifferentEnum": { "title": "DifferentEnum", @@ -721,7 +725,8 @@ "$ref": "#/components/schemas/ValidationError" } } - } + }, + "additionalProperties": false }, "ValidationError": { "title": "ValidationError", @@ -743,7 +748,8 @@ "title": "Error Type", "type": "string" } - } + }, + "additionalProperties": false }, "ModelWithUnionProperty": { "title": "ModelWithUnionProperty", @@ -755,6 +761,50 @@ {"$ref": "#/components/schemas/AnIntEnum"} ] } + }, + "additionalProperties": false + }, + "FreeFormModel": { + "title": "FreeFormModel", + "type": "object" + }, + "ModelWithAdditionalPropertiesInlined": { + "title": "ModelWithAdditionalPropertiesInlined", + "type": "object", + "properties": { + "a_number": { + "type": "number" + } + }, + "additionalProperties": { + "type": "object", + "properties": { + "extra_props_prop": {"type": "string"} + }, + "additionalProperties": {} + } + }, + "ModelWithPrimitiveAdditionalProperties": { + "title": "ModelWithPrimitiveAdditionalProperties", + "type": "object", + "properties": { + "a_date_holder": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "date-time" + } + } + }, + "additionalProperties": { + "type": "string" + } + }, + "ModelWithAdditionalPropertiesRefed": { + "title": "ModelWithAdditionalPropertiesRefed", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/AnEnum" } } } diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 4b71df5a2..98fc4fc14 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -255,6 +255,27 @@ def build_model_property( optional_properties.append(prop) relative_imports.update(prop.get_imports(prefix="..")) + additional_properties: Union[bool, Property, PropertyError] + if data.additionalProperties is None: + additional_properties = True + elif isinstance(data.additionalProperties, bool): + additional_properties = data.additionalProperties + elif isinstance(data.additionalProperties, oai.Schema) and not any(data.additionalProperties.dict().values()): + # An empty schema + additional_properties = True + else: + assert isinstance(data.additionalProperties, (oai.Schema, oai.Reference)) + additional_properties, schemas = property_from_data( + name="AdditionalProperty", + required=True, # in the sense that if present in the dict will not be None + data=data.additionalProperties, + schemas=schemas, + parent_name=class_name, + ) + if isinstance(additional_properties, PropertyError): + return additional_properties, schemas + relative_imports.update(additional_properties.get_imports(prefix="..")) + prop = ModelProperty( reference=ref, required_properties=required_properties, @@ -265,6 +286,7 @@ def build_model_property( nullable=data.nullable, required=required, name=name, + additional_properties=additional_properties, ) schemas = attr.evolve(schemas, models={**schemas.models, prop.reference.class_name: prop}) return prop, schemas diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index ca36171af..084017a41 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -1,4 +1,4 @@ -from typing import ClassVar, List, Set +from typing import ClassVar, List, Set, Union import attr @@ -16,6 +16,7 @@ class ModelProperty(Property): optional_properties: List[Property] description: str relative_imports: Set[str] + additional_properties: Union[bool, Property] template: ClassVar[str] = "model_property.pyi" diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index cbed730f3..c66ba46a3 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -1,5 +1,10 @@ from typing import Any, Dict +{% if model.additional_properties %} +from typing import List + +{% endif %} + import attr from ..types import UNSET, Unset @@ -9,6 +14,10 @@ from ..types import UNSET, Unset {% endfor %} +{% if model.additional_properties %} +{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %} +{% endif %} + @attr.s(auto_attribs=True) class {{ model.reference.class_name }}: """ {{ model.description }} """ @@ -22,6 +31,10 @@ class {{ model.reference.class_name }}: {{ property.to_string() }} {% endif %} {% endfor %} + {% if model.additional_properties %} + additional_properties: Dict[str, {{ additional_property_type }}] = attr.ib(init=False, factory=dict) + {% endif %} + def to_dict(self) -> Dict[str, Any]: {% for property in model.required_properties + model.optional_properties %} @@ -33,14 +46,23 @@ class {{ model.reference.class_name }}: {% endif %} {% endfor %} - - field_dict = { + field_dict: Dict[str, Any] = {} + {% if model.additional_properties %} + {% if model.additional_properties.template %} + {% from "property_templates/" + model.additional_properties.template import transform %} + for prop_name, prop in self.additional_properties.items(): + {{ transform(model.additional_properties, "prop", "field_dict[prop_name]") | indent(4) }} + {% else %} + field_dict.update(self.additional_properties) + {% endif %} + {% endif %} + field_dict.update({ {% for property in model.required_properties + model.optional_properties %} {% if property.required %} "{{ property.name }}": {{ property.python_name }}, {% endif %} {% endfor %} - } + }) {% for property in model.optional_properties %} {% if not property.required %} if {{ property.python_name }} is not UNSET: @@ -51,12 +73,13 @@ class {{ model.reference.class_name }}: return field_dict @staticmethod - def from_dict(d: Dict[str, Any]) -> "{{ model.reference.class_name }}": + def from_dict(src_dict: Dict[str, Any]) -> "{{ model.reference.class_name }}": + d = src_dict.copy() {% for property in model.required_properties + model.optional_properties %} {% if property.required %} - {% set property_source = 'd["' + property.name + '"]' %} + {% set property_source = 'd.pop("' + property.name + '")' %} {% else %} - {% set property_source = 'd.get("' + property.name + '", UNSET)' %} + {% set property_source = 'd.pop("' + property.name + '", UNSET)' %} {% endif %} {% if property.template %} {% from "property_templates/" + property.template import construct %} @@ -66,8 +89,42 @@ class {{ model.reference.class_name }}: {% endif %} {% endfor %} - return {{ model.reference.class_name }}( + {{model.reference.module_name}} = {{ model.reference.class_name }}( {% for property in model.required_properties + model.optional_properties %} {{ property.python_name }}={{ property.python_name }}, {% endfor %} ) + +{% if model.additional_properties %} + {% if model.additional_properties.template %} + {% from "property_templates/" + model.additional_properties.template import construct %} + additional_properties = {} + for prop_name, prop_dict in d.items(): + {{ construct(model.additional_properties, "prop_dict") | indent(12) }} + additional_properties[prop_name] = {{ model.additional_properties.python_name }} + + {{model.reference.module_name}}.additional_properties = additional_properties + {% else %} + {{model.reference.module_name}}.additional_properties = d + {% endif %} +{% endif %} + return {{model.reference.module_name}} + + {% if model.additional_properties %} + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> {{ additional_property_type }}: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: {{ additional_property_type }}) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties + {% endif %} + diff --git a/openapi_python_client/templates/property_templates/date_property.pyi b/openapi_python_client/templates/property_templates/date_property.pyi index b8b12ce57..22624c7e5 100644 --- a/openapi_python_client/templates/property_templates/date_property.pyi +++ b/openapi_python_client/templates/property_templates/date_property.pyi @@ -3,8 +3,9 @@ {{ property.python_name }} = isoparse({{ source }}).date() {% else %} {{ property.python_name }} = {{ initial_value }} -if {{ source }} is not None: - {{ property.python_name }} = isoparse(cast(str, {{ source }})).date() +_{{ property.python_name }} = {{ source }} +if _{{ property.python_name }} is not None: + {{ property.python_name }} = isoparse(cast(str, _{{ property.python_name }})).date() {% endif %} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/datetime_property.pyi b/openapi_python_client/templates/property_templates/datetime_property.pyi index b7c822a62..353978fa0 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.pyi +++ b/openapi_python_client/templates/property_templates/datetime_property.pyi @@ -3,8 +3,9 @@ {{ property.python_name }} = isoparse({{ source }}) {% else %} {{ property.python_name }} = {{ initial_value }} -if {{ source }} is not None: - {{ property.python_name }} = isoparse(cast(str, {{ source }})) +_{{ property.python_name }} = {{ source }} +if _{{ property.python_name }} is not None: + {{ property.python_name }} = isoparse(cast(str, _{{ property.python_name }})) {% endif %} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/dict_property.pyi b/openapi_python_client/templates/property_templates/dict_property.pyi index b55191f20..36a6b4292 100644 --- a/openapi_python_client/templates/property_templates/dict_property.pyi +++ b/openapi_python_client/templates/property_templates/dict_property.pyi @@ -3,8 +3,9 @@ {{ property.python_name }} = {{ source }} {% else %} {{ property.python_name }} = {{ initial_value }} -if {{ source }} is not None: - {{ property.python_name }} = {{ source }} +_{{ property.python_name }} = {{ source }} +if _{{ property.python_name }} is not None: + {{ property.python_name }} = _{{ property.python_name }} {% endif %} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/enum_property.pyi b/openapi_python_client/templates/property_templates/enum_property.pyi index d098bb471..4765a6fd5 100644 --- a/openapi_python_client/templates/property_templates/enum_property.pyi +++ b/openapi_python_client/templates/property_templates/enum_property.pyi @@ -3,8 +3,9 @@ {{ property.python_name }} = {{ property.reference.class_name }}({{ source }}) {% else %} {{ property.python_name }} = {{ initial_value }} -if {{ source }} is not None: - {{ property.python_name }} = {{ property.reference.class_name }}({{ source }}) +_{{ property.python_name }} = {{ source }} +if _{{ property.python_name }} is not None: + {{ property.python_name }} = {{ property.reference.class_name }}(_{{ property.python_name }}) {% endif %} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/list_property.pyi b/openapi_python_client/templates/property_templates/list_property.pyi index f06f0429d..d05a13960 100644 --- a/openapi_python_client/templates/property_templates/list_property.pyi +++ b/openapi_python_client/templates/property_templates/list_property.pyi @@ -3,10 +3,11 @@ {% if inner_property.template %} {% set inner_source = inner_property.python_name + "_data" %} {{ property.python_name }} = {{ initial_value }} +_{{ property.python_name }} = {{ source }} {% if property.required %} -for {{ inner_source }} in ({{ source }}): +for {{ inner_source }} in (_{{ property.python_name }}): {% else %} -for {{ inner_source }} in ({{ source }} or []): +for {{ inner_source }} in (_{{ property.python_name }} or []): {% endif %} {% from "property_templates/" + inner_property.template import construct %} {{ construct(inner_property, inner_source) | indent(4) }} diff --git a/openapi_python_client/templates/property_templates/model_property.pyi b/openapi_python_client/templates/property_templates/model_property.pyi index eedb22235..a870a6528 100644 --- a/openapi_python_client/templates/property_templates/model_property.pyi +++ b/openapi_python_client/templates/property_templates/model_property.pyi @@ -2,9 +2,16 @@ {% if property.required %} {{ property.python_name }} = {{ property.reference.class_name }}.from_dict({{ source }}) {% else %} +{% if initial_value != None %} {{ property.python_name }} = {{ initial_value }} -if {{ source }} is not None: - {{ property.python_name }} = {{ property.reference.class_name }}.from_dict(cast(Dict[str, Any], {{ source }})) +{% elif property.nullable %} +{{ property.python_name }} = None +{% else %} +{{ property.python_name }} = UNSET +{% endif %} +_{{ property.python_name }} = {{source}} +if _{{ property.python_name }} is not None and not isinstance({{ property.python_name }}, Unset): + {{ property.python_name }} = {{ property.reference.class_name }}.from_dict(cast(Dict[str, Any], _{{ property.python_name }})) {% endif %} {% endmacro %} @@ -16,7 +23,7 @@ if {{ source }} is not None: {{ destination }} = {{ source }}.to_dict() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = UNSET +{{ destination }}{% if declare_type %}: Union[{% if property.nullable %}None, {% endif %}Unset, Dict[str, Any]]{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} {{ destination }} = {{ source }}.to_dict() if {{ source }} else None diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 96047c60f..b2dd1bb2f 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -588,6 +588,7 @@ def test_property_from_data_ref_model(self): optional_properties=[], description="", relative_imports=set(), + additional_properties=False, ) schemas = Schemas(models={class_name: existing_model}) @@ -603,6 +604,7 @@ def test_property_from_data_ref_model(self): optional_properties=[], description="", relative_imports=set(), + additional_properties=False, ) assert schemas == new_schemas @@ -1039,7 +1041,20 @@ def test_build_enums(mocker): build_model_property.assert_not_called() -def test_build_model_property(): +@pytest.mark.parametrize( + "additional_properties_schema, expected_additional_properties", + [ + (True, True), + (oai.Schema.construct(), True), + (None, True), + (False, False), + ( + oai.Schema.construct(type="string"), + StringProperty(name="AdditionalProperty", required=True, nullable=False, default=None), + ), + ], +) +def test_build_model_property(additional_properties_schema, expected_additional_properties): from openapi_python_client.parser.properties import Schemas, build_model_property data = oai.Schema.construct( @@ -1051,6 +1066,7 @@ def test_build_model_property(): }, description="A class called MyModel", nullable=False, + additionalProperties=additional_properties_schema, ) schemas = Schemas(models={"OtherModel": None}) @@ -1083,6 +1099,7 @@ def test_build_model_property(): "from ..types import UNSET, Unset", "from typing import Union", }, + additional_properties=expected_additional_properties, ) @@ -1108,6 +1125,30 @@ def test_build_model_property_bad_prop(): assert err == PropertyError(detail="unknown type not_real", data=oai.Schema(type="not_real")) +def test_build_model_property_bad_additional_props(): + from openapi_python_client.parser.properties import Schemas, build_model_property + + additional_properties = oai.Schema( + type="object", + properties={ + "bad": oai.Schema(type="not_real"), + }, + ) + data = oai.Schema(additionalProperties=additional_properties) + schemas = Schemas(models={"OtherModel": None}) + + err, new_schemas = build_model_property( + data=data, + name="prop", + schemas=schemas, + required=True, + parent_name=None, + ) + + assert new_schemas == schemas + assert err == PropertyError(detail="unknown type not_real", data=oai.Schema(type="not_real")) + + def test_build_enum_property_conflict(mocker): from openapi_python_client.parser.properties import Schemas, build_enum_property diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 72c8f27f1..1024ef179 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -27,6 +27,7 @@ def test_get_type_string(no_optional, nullable, required, expected): optional_properties=[], required_properties=[], relative_imports=set(), + additional_properties=False, ) assert prop.get_type_string(no_optional=no_optional) == expected @@ -45,6 +46,7 @@ def test_get_imports(): optional_properties=[], required_properties=[], relative_imports=set(), + additional_properties=False, ) assert prop.get_imports(prefix="..") == {