From a96621d9c8a1b400946b8ae503f21650772bb5d8 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 25 Nov 2020 12:50:40 -0800 Subject: [PATCH 01/20] Add additional property support by subscripting model --- .../parser/properties/__init__.py | 15 +++++ .../parser/properties/model_property.py | 3 +- openapi_python_client/templates/model.pyi | 60 ++++++++++++++++--- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 4b71df5a2..dce882480 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -255,6 +255,20 @@ def build_model_property( optional_properties.append(prop) relative_imports.update(prop.get_imports(prefix="..")) + if data.additionalProperties is not None and not isinstance(data.additionalProperties, bool): + additional_properties, schemas = property_from_data( + name="AdditionalProperties", + required=False, + 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="..")) + else: + additional_properties = data.additionalProperties + prop = ModelProperty( reference=ref, required_properties=required_properties, @@ -265,6 +279,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..dfad72aa9 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, Optional, Set, Union import attr @@ -16,6 +16,7 @@ class ModelProperty(Property): optional_properties: List[Property] description: str relative_imports: Set[str] + additional_properties: Optional[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..1cf573d6e 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(no_optional=True) %} +{% 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,17 @@ class {{ model.reference.class_name }}: {% endif %} {% endfor %} - - field_dict = { + field_dict: Dict[str, Any] = {} + {% if model.additional_properties %} + field_dict.update(self._additional_properties) + {% 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 +67,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 +83,37 @@ 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 %} + {{model.reference.module_name}}._additional_properties.update(d) + {% endif %} + return {{model.reference.module_name}} + + {% if model.additional_properties %} + + @property + def additional_properties(self) -> Dict[str, {{ additional_property_type }}]: + return self._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 %} + From f8ca735ba9daf12959b903c35db9da0012cedff4 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 25 Nov 2020 12:59:27 -0800 Subject: [PATCH 02/20] Update e2e golden records --- .../custom_e2e/models/a_model.py | 42 +++++++++++-------- .../body_upload_file_tests_upload_post.py | 18 +++++--- .../models/http_validation_error.py | 12 ++++-- .../models/model_with_union_property.py | 13 ++++-- .../models/test_inline_objects_json_body.py | 12 ++++-- .../test_inline_objects_response_200.py | 12 ++++-- .../custom_e2e/models/validation_error.py | 26 +++++++----- .../my_test_api_client/models/a_model.py | 42 +++++++++++-------- .../body_upload_file_tests_upload_post.py | 18 +++++--- .../models/http_validation_error.py | 12 ++++-- .../models/model_with_union_property.py | 13 ++++-- .../models/test_inline_objects_json_body.py | 12 ++++-- .../test_inline_objects_response_200.py | 12 ++++-- .../models/validation_error.py | 26 +++++++----- 14 files changed, 170 insertions(+), 100 deletions(-) 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..6bfca16ff 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,14 +91,14 @@ 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 []: + for nested_list_of_enums_item_data in d.pop("nested_list_of_enums", UNSET) 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_item = DifferentEnum(nested_list_of_enums_item_item_data) @@ -103,15 +107,15 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat 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 +126,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/http_validation_error.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/http_validation_error.py index 9d29faa4d..e9d4ea11c 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,24 @@ 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 []: + for detail_item_data in d.pop("detail", UNSET) 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_union_property.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py index 2ca963e01..d9508293f 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,14 +27,17 @@ 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] @@ -52,8 +55,10 @@ def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: 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/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index f523e36f6..6bfca16ff 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,14 +91,14 @@ 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 []: + for nested_list_of_enums_item_data in d.pop("nested_list_of_enums", UNSET) 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_item = DifferentEnum(nested_list_of_enums_item_item_data) @@ -103,15 +107,15 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat 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 +126,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/http_validation_error.py b/end_to_end_tests/golden-record/my_test_api_client/models/http_validation_error.py index 9d29faa4d..e9d4ea11c 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,24 @@ 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 []: + for detail_item_data in d.pop("detail", UNSET) 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_union_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py index 2ca963e01..d9508293f 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,14 +27,17 @@ 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] @@ -52,8 +55,10 @@ def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: 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 From 62fad1e41ffeaa702d3c30023069edadea05d874 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 25 Nov 2020 13:00:50 -0800 Subject: [PATCH 03/20] Add models with additionalProperties to golden record --- .../custom_e2e/models/__init__.py | 6 ++ .../custom_e2e/models/free_form_model.py | 46 ++++++++++++++ ...odel_with_additional_properties_inlined.py | 61 +++++++++++++++++++ ...roperties_inlined_additional_properties.py | 35 +++++++++++ .../model_with_additional_properties_refed.py | 48 +++++++++++++++ .../my_test_api_client/models/__init__.py | 6 ++ .../models/free_form_model.py | 46 ++++++++++++++ ...odel_with_additional_properties_inlined.py | 61 +++++++++++++++++++ ...roperties_inlined_additional_properties.py | 35 +++++++++++ .../model_with_additional_properties_refed.py | 48 +++++++++++++++ end_to_end_tests/openapi.json | 27 ++++++++ 11 files changed, 419 insertions(+) create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/free_form_model.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_refed.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py 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..722bdb2b5 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,13 @@ 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_properties import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties, +) +from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed 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/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..428c59bab --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/free_form_model.py @@ -0,0 +1,46 @@ +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.update(d) + return free_form_model + + @property + def additional_properties(self) -> Dict[str, Any]: + return self._additional_properties + + @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_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..21d9a3df7 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py @@ -0,0 +1,61 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.model_with_additional_properties_inlined_additional_properties import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlined: + """ """ + + a_number: Union[Unset, float] = UNSET + _additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + init=False, factory=dict + ) + + def to_dict(self) -> Dict[str, Any]: + a_number = self.a_number + + field_dict: Dict[str, Any] = {} + field_dict.update(self._additional_properties) + 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, + ) + + model_with_additional_properties_inlined._additional_properties.update(d) + return model_with_additional_properties_inlined + + @property + def additional_properties(self) -> Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties]: + return self._additional_properties + + @property + def additional_keys(self) -> List[str]: + return list(self._additional_properties.keys()) + + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: + return self._additional_properties[key] + + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> 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_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py new file mode 100644 index 000000000..c8125e0e5 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py @@ -0,0 +1,35 @@ +from typing import Any, Dict, Union + +import attr + +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlinedAdditionalProperties: + """ """ + + extra_props_prop: Union[Unset, str] = UNSET + + def to_dict(self) -> Dict[str, Any]: + extra_props_prop = self.extra_props_prop + + field_dict: Dict[str, Any] = {} + 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]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperties": + d = src_dict.copy() + extra_props_prop = d.pop("extra_props_prop", UNSET) + + model_with_additional_properties_inlined_additional_properties = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties( + extra_props_prop=extra_props_prop, + ) + ) + + return model_with_additional_properties_inlined_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..6a250e5c3 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_refed.py @@ -0,0 +1,48 @@ +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] = {} + field_dict.update(self._additional_properties) + 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() + + model_with_additional_properties_refed._additional_properties.update(d) + return model_with_additional_properties_refed + + @property + def additional_properties(self) -> Dict[str, AnEnum]: + return self._additional_properties + + @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/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index a01ec4327..722bdb2b5 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,13 @@ 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_properties import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties, +) +from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed 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/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..428c59bab --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/free_form_model.py @@ -0,0 +1,46 @@ +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.update(d) + return free_form_model + + @property + def additional_properties(self) -> Dict[str, Any]: + return self._additional_properties + + @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_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..21d9a3df7 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py @@ -0,0 +1,61 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.model_with_additional_properties_inlined_additional_properties import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties, +) +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlined: + """ """ + + a_number: Union[Unset, float] = UNSET + _additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + init=False, factory=dict + ) + + def to_dict(self) -> Dict[str, Any]: + a_number = self.a_number + + field_dict: Dict[str, Any] = {} + field_dict.update(self._additional_properties) + 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, + ) + + model_with_additional_properties_inlined._additional_properties.update(d) + return model_with_additional_properties_inlined + + @property + def additional_properties(self) -> Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties]: + return self._additional_properties + + @property + def additional_keys(self) -> List[str]: + return list(self._additional_properties.keys()) + + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: + return self._additional_properties[key] + + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> 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_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py new file mode 100644 index 000000000..c8125e0e5 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py @@ -0,0 +1,35 @@ +from typing import Any, Dict, Union + +import attr + +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class ModelWithAdditionalPropertiesInlinedAdditionalProperties: + """ """ + + extra_props_prop: Union[Unset, str] = UNSET + + def to_dict(self) -> Dict[str, Any]: + extra_props_prop = self.extra_props_prop + + field_dict: Dict[str, Any] = {} + 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]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperties": + d = src_dict.copy() + extra_props_prop = d.pop("extra_props_prop", UNSET) + + model_with_additional_properties_inlined_additional_properties = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperties( + extra_props_prop=extra_props_prop, + ) + ) + + return model_with_additional_properties_inlined_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..6a250e5c3 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_refed.py @@ -0,0 +1,48 @@ +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] = {} + field_dict.update(self._additional_properties) + 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() + + model_with_additional_properties_refed._additional_properties.update(d) + return model_with_additional_properties_refed + + @property + def additional_properties(self) -> Dict[str, AnEnum]: + return self._additional_properties + + @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/openapi.json b/end_to_end_tests/openapi.json index 426127f17..4c3c5d864 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -756,6 +756,33 @@ ] } } + }, + "FreeFormModel": { + "title": "FreeFormModel", + "type": "object", + "additionalProperties": true + }, + "ModelWithAdditionalPropertiesInlined": { + "title": "ModelWithAdditionalPropertiesInlined", + "type": "object", + "properties": { + "a_number": { + "type": "number" + } + }, + "additionalProperties": { + "type": "object", + "properties": { + "extra_props_prop": {"type": "string"} + } + } + }, + "ModelWithAdditionalPropertiesRefed": { + "title": "ModelWithAdditionalPropertiesRefed", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/AnEnum" + } } } } From f6028d39a3e368438599699569446652e4ad14c5 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Mon, 30 Nov 2020 09:44:55 -0800 Subject: [PATCH 04/20] PR Feedback: Set additional properties directly --- .../golden-record-custom/custom_e2e/models/free_form_model.py | 2 +- .../models/model_with_additional_properties_inlined.py | 2 +- .../custom_e2e/models/model_with_additional_properties_refed.py | 2 +- .../golden-record/my_test_api_client/models/free_form_model.py | 2 +- .../models/model_with_additional_properties_inlined.py | 2 +- .../models/model_with_additional_properties_refed.py | 2 +- openapi_python_client/templates/model.pyi | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) 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 index 428c59bab..aff740239 100644 --- 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 @@ -22,7 +22,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": d = src_dict.copy() free_form_model = FreeFormModel() - free_form_model._additional_properties.update(d) + free_form_model._additional_properties = d return free_form_model @property 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 index 21d9a3df7..7b43fca52 100644 --- 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 @@ -37,7 +37,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined._additional_properties.update(d) + model_with_additional_properties_inlined._additional_properties = d return model_with_additional_properties_inlined @property 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 index 6a250e5c3..a7086070c 100644 --- 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 @@ -24,7 +24,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed._additional_properties.update(d) + model_with_additional_properties_refed._additional_properties = d return model_with_additional_properties_refed @property 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 index 428c59bab..aff740239 100644 --- 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 @@ -22,7 +22,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": d = src_dict.copy() free_form_model = FreeFormModel() - free_form_model._additional_properties.update(d) + free_form_model._additional_properties = d return free_form_model @property 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 index 21d9a3df7..7b43fca52 100644 --- 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 @@ -37,7 +37,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined._additional_properties.update(d) + model_with_additional_properties_inlined._additional_properties = d return model_with_additional_properties_inlined @property 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 index 6a250e5c3..a7086070c 100644 --- 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 @@ -24,7 +24,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed._additional_properties.update(d) + model_with_additional_properties_refed._additional_properties = d return model_with_additional_properties_refed @property diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index 1cf573d6e..fc2c1bcb4 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -90,7 +90,7 @@ class {{ model.reference.class_name }}: ) {% if model.additional_properties %} - {{model.reference.module_name}}._additional_properties.update(d) + {{model.reference.module_name}}._additional_properties = d {% endif %} return {{model.reference.module_name}} From 4c0fbd0ff7ea58271b48fe4e66a03078d9639855 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Mon, 30 Nov 2020 09:50:39 -0800 Subject: [PATCH 05/20] PR Feedback: Just make additionalProperties public --- .../custom_e2e/models/free_form_model.py | 20 +++++++----------- ...odel_with_additional_properties_inlined.py | 20 +++++++----------- .../model_with_additional_properties_refed.py | 20 +++++++----------- .../models/free_form_model.py | 20 +++++++----------- ...odel_with_additional_properties_inlined.py | 20 +++++++----------- .../model_with_additional_properties_refed.py | 20 +++++++----------- openapi_python_client/templates/model.pyi | 21 +++++++------------ 7 files changed, 56 insertions(+), 85 deletions(-) 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 index aff740239..0e1b93899 100644 --- 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 @@ -7,12 +7,12 @@ class FreeFormModel: """ """ - _additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + 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(self.additional_properties) field_dict.update({}) return field_dict @@ -22,25 +22,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": d = src_dict.copy() free_form_model = FreeFormModel() - free_form_model._additional_properties = d + free_form_model.additional_properties = d return free_form_model - @property - def additional_properties(self) -> Dict[str, Any]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: Any) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + return key in self.additional_properties 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 index 7b43fca52..031006de3 100644 --- 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 @@ -13,7 +13,7 @@ class ModelWithAdditionalPropertiesInlined: """ """ a_number: Union[Unset, float] = UNSET - _additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( init=False, factory=dict ) @@ -21,7 +21,7 @@ def to_dict(self) -> Dict[str, Any]: a_number = self.a_number field_dict: Dict[str, Any] = {} - field_dict.update(self._additional_properties) + field_dict.update(self.additional_properties) field_dict.update({}) if a_number is not UNSET: field_dict["a_number"] = a_number @@ -37,25 +37,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined._additional_properties = d + model_with_additional_properties_inlined.additional_properties = d return model_with_additional_properties_inlined - @property - def additional_properties(self) -> Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + 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 index a7086070c..7c9a86cb4 100644 --- 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 @@ -9,12 +9,12 @@ class ModelWithAdditionalPropertiesRefed: """ """ - _additional_properties: Dict[str, AnEnum] = attr.ib(init=False, factory=dict) + additional_properties: Dict[str, AnEnum] = 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(self.additional_properties) field_dict.update({}) return field_dict @@ -24,25 +24,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed._additional_properties = d + model_with_additional_properties_refed.additional_properties = d return model_with_additional_properties_refed - @property - def additional_properties(self) -> Dict[str, AnEnum]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> AnEnum: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: AnEnum) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + return key in self.additional_properties 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 index aff740239..0e1b93899 100644 --- 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 @@ -7,12 +7,12 @@ class FreeFormModel: """ """ - _additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + 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(self.additional_properties) field_dict.update({}) return field_dict @@ -22,25 +22,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "FreeFormModel": d = src_dict.copy() free_form_model = FreeFormModel() - free_form_model._additional_properties = d + free_form_model.additional_properties = d return free_form_model - @property - def additional_properties(self) -> Dict[str, Any]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> Any: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: Any) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + 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.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py index 7b43fca52..031006de3 100644 --- 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 @@ -13,7 +13,7 @@ class ModelWithAdditionalPropertiesInlined: """ """ a_number: Union[Unset, float] = UNSET - _additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( init=False, factory=dict ) @@ -21,7 +21,7 @@ def to_dict(self) -> Dict[str, Any]: a_number = self.a_number field_dict: Dict[str, Any] = {} - field_dict.update(self._additional_properties) + field_dict.update(self.additional_properties) field_dict.update({}) if a_number is not UNSET: field_dict["a_number"] = a_number @@ -37,25 +37,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined._additional_properties = d + model_with_additional_properties_inlined.additional_properties = d return model_with_additional_properties_inlined - @property - def additional_properties(self) -> Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + 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 index a7086070c..7c9a86cb4 100644 --- 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 @@ -9,12 +9,12 @@ class ModelWithAdditionalPropertiesRefed: """ """ - _additional_properties: Dict[str, AnEnum] = attr.ib(init=False, factory=dict) + additional_properties: Dict[str, AnEnum] = 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(self.additional_properties) field_dict.update({}) return field_dict @@ -24,25 +24,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed._additional_properties = d + model_with_additional_properties_refed.additional_properties = d return model_with_additional_properties_refed - @property - def additional_properties(self) -> Dict[str, AnEnum]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> AnEnum: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: AnEnum) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + return key in self.additional_properties diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index fc2c1bcb4..e846629d2 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -32,7 +32,7 @@ class {{ model.reference.class_name }}: {% endif %} {% endfor %} {% if model.additional_properties %} - _additional_properties: Dict[str, {{ additional_property_type }}] = attr.ib(init=False, factory=dict) + additional_properties: Dict[str, {{ additional_property_type }}] = attr.ib(init=False, factory=dict) {% endif %} @@ -48,7 +48,7 @@ class {{ model.reference.class_name }}: field_dict: Dict[str, Any] = {} {% if model.additional_properties %} - field_dict.update(self._additional_properties) + field_dict.update(self.additional_properties) {% endif %} field_dict.update({ {% for property in model.required_properties + model.optional_properties %} @@ -90,30 +90,25 @@ class {{ model.reference.class_name }}: ) {% if model.additional_properties %} - {{model.reference.module_name}}._additional_properties = d + {{model.reference.module_name}}.additional_properties = d {% endif %} return {{model.reference.module_name}} {% if model.additional_properties %} - - @property - def additional_properties(self) -> Dict[str, {{ additional_property_type }}]: - return self._additional_properties - @property def additional_keys(self) -> List[str]: - return list(self._additional_properties.keys()) + return list(self.additional_properties.keys()) def __getitem__(self, key: str) -> {{ additional_property_type }}: - return self._additional_properties[key] + return self.additional_properties[key] def __setitem__(self, key: str, value: {{ additional_property_type }}) -> None: - self._additional_properties[key] = value + self.additional_properties[key] = value def __delitem__(self, key: str) -> None: - del self._additional_properties[key] + del self.additional_properties[key] def __contains__(self, key: str) -> bool: - return key in self._additional_properties + return key in self.additional_properties {% endif %} From b13d2829e20933513ce3d01792ed3f4ccfaa2e5a Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Mon, 30 Nov 2020 13:59:34 -0800 Subject: [PATCH 06/20] Handle edge cases of additionalProperties to interpret per the spec --- openapi_python_client/parser/properties/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index dce882480..c601753fc 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -255,7 +255,15 @@ def build_model_property( optional_properties.append(prop) relative_imports.update(prop.get_imports(prefix="..")) - if data.additionalProperties is not None and not isinstance(data.additionalProperties, bool): + 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="AdditionalProperties", required=False, @@ -266,8 +274,6 @@ def build_model_property( if isinstance(additional_properties, PropertyError): return additional_properties, schemas relative_imports.update(additional_properties.get_imports(prefix="..")) - else: - additional_properties = data.additionalProperties prop = ModelProperty( reference=ref, From 7b72c9a6b4b16f88028a236af4d71cb7a8d85d74 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Mon, 30 Nov 2020 14:16:13 -0800 Subject: [PATCH 07/20] Update golden record's spec to specify no additional props on existing models, test edge case parsing --- ...roperties_inlined_additional_properties.py | 21 ++++++++++++++- ...roperties_inlined_additional_properties.py | 21 ++++++++++++++- end_to_end_tests/openapi.json | 27 ++++++++++++------- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py index c8125e0e5..388dddc54 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Union +from typing import Any, Dict, List, Union import attr @@ -10,11 +10,13 @@ class ModelWithAdditionalPropertiesInlinedAdditionalProperties: """ """ 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 @@ -32,4 +34,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined ) ) + model_with_additional_properties_inlined_additional_properties.additional_properties = d return model_with_additional_properties_inlined_additional_properties + + @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_inlined_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py index c8125e0e5..388dddc54 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Union +from typing import Any, Dict, List, Union import attr @@ -10,11 +10,13 @@ class ModelWithAdditionalPropertiesInlinedAdditionalProperties: """ """ 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 @@ -32,4 +34,21 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined ) ) + model_with_additional_properties_inlined_additional_properties.additional_properties = d return model_with_additional_properties_inlined_additional_properties + + @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/openapi.json b/end_to_end_tests/openapi.json index 4c3c5d864..8dc4c8510 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,12 +761,12 @@ {"$ref": "#/components/schemas/AnIntEnum"} ] } - } + }, + "additionalProperties": false }, "FreeFormModel": { "title": "FreeFormModel", - "type": "object", - "additionalProperties": true + "type": "object" }, "ModelWithAdditionalPropertiesInlined": { "title": "ModelWithAdditionalPropertiesInlined", @@ -774,7 +780,8 @@ "type": "object", "properties": { "extra_props_prop": {"type": "string"} - } + }, + "additionalProperties": {} } }, "ModelWithAdditionalPropertiesRefed": { From 3a827f1b9952e069c8e1a40ed6d3ead0af79912d Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Mon, 30 Nov 2020 18:42:12 -0800 Subject: [PATCH 08/20] Support modeled additionalProperties --- .../parser/properties/__init__.py | 2 +- openapi_python_client/templates/model.pyi | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index c601753fc..fd7b57c30 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -266,7 +266,7 @@ def build_model_property( assert isinstance(data.additionalProperties, (oai.Schema, oai.Reference)) additional_properties, schemas = property_from_data( name="AdditionalProperties", - required=False, + required=True, # in the sense that if present in the dict will not be None data=data.additionalProperties, schemas=schemas, parent_name=class_name, diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index e846629d2..e64b6f8ae 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -15,7 +15,7 @@ from ..types import UNSET, Unset {% if model.additional_properties %} -{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string(no_optional=True) %} +{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %} {% endif %} @attr.s(auto_attribs=True) @@ -48,8 +48,14 @@ class {{ model.reference.class_name }}: 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 %} @@ -89,9 +95,19 @@ class {{ model.reference.class_name }}: {% endfor %} ) - {% if model.additional_properties %} +{% if model.additional_properties %} + {% if model.additional_properties.template %} + {% from "property_templates/" + model.additional_properties.template import construct %} + additional_properties_dict = {} + for prop_name, prop_dict in d.items(): + {{ construct(model.additional_properties, "prop_dict") | indent(12) }} + additional_properties_dict[prop_name] = {{ model.additional_properties.python_name }} + + {{model.reference.module_name}}.additional_properties = additional_properties_dict + {% else %} {{model.reference.module_name}}.additional_properties = d - {% endif %} + {% endif %} +{% endif %} return {{model.reference.module_name}} {% if model.additional_properties %} From 78015fce70f8949a06dcaf96e25e2524361ec84b Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 2 Dec 2020 18:59:09 -0800 Subject: [PATCH 09/20] Typing fixes for model properties --- .../templates/property_templates/model_property.pyi | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 From e3e0fb414256f98b1fc73dbfeb40c6904953a2f8 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 2 Dec 2020 18:59:48 -0800 Subject: [PATCH 10/20] Add ModelWithPrimitiveAdditionalProperties to e2e test spec --- end_to_end_tests/openapi.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 8dc4c8510..1cbfd9597 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -784,6 +784,22 @@ "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", From 45ce82b1db222e2f539f504cbf30605f60a306d0 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 2 Dec 2020 19:00:29 -0800 Subject: [PATCH 11/20] Regen golden records --- .../custom_e2e/models/__init__.py | 2 + ...odel_with_additional_properties_inlined.py | 12 +++- .../model_with_additional_properties_refed.py | 12 +++- ...el_with_primitive_additional_properties.py | 62 +++++++++++++++++++ ...ive_additional_properties_a_date_holder.py | 52 ++++++++++++++++ .../my_test_api_client/models/__init__.py | 2 + ...odel_with_additional_properties_inlined.py | 12 +++- .../model_with_additional_properties_refed.py | 12 +++- ...el_with_primitive_additional_properties.py | 62 +++++++++++++++++++ ...ive_additional_properties_a_date_holder.py | 52 ++++++++++++++++ 10 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties_a_date_holder.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties_a_date_holder.py 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 722bdb2b5..939c56666 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 @@ -12,6 +12,8 @@ ModelWithAdditionalPropertiesInlinedAdditionalProperties, ) 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/model_with_additional_properties_inlined.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined.py index 031006de3..4abf1bb24 100644 --- 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 @@ -21,7 +21,9 @@ def to_dict(self) -> Dict[str, Any]: a_number = self.a_number field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) + 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 @@ -37,7 +39,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined.additional_properties = d + additional_properties_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = ModelWithAdditionalPropertiesInlinedAdditionalProperties.from_dict(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_additional_properties_inlined.additional_properties = additional_properties_dict return model_with_additional_properties_inlined @property 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 index 7c9a86cb4..a689e8d6e 100644 --- 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 @@ -14,7 +14,9 @@ class ModelWithAdditionalPropertiesRefed: def to_dict(self) -> Dict[str, Any]: field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.value + field_dict.update({}) return field_dict @@ -24,7 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed.additional_properties = d + additional_properties_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = AnEnum(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_additional_properties_refed.additional_properties = additional_properties_dict return model_with_additional_properties_refed @property 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..4b327a446 --- /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_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = isoparse(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties_dict + 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/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index 722bdb2b5..939c56666 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 @@ -12,6 +12,8 @@ ModelWithAdditionalPropertiesInlinedAdditionalProperties, ) 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/model_with_additional_properties_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined.py index 031006de3..4abf1bb24 100644 --- 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 @@ -21,7 +21,9 @@ def to_dict(self) -> Dict[str, Any]: a_number = self.a_number field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) + 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 @@ -37,7 +39,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - model_with_additional_properties_inlined.additional_properties = d + additional_properties_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = ModelWithAdditionalPropertiesInlinedAdditionalProperties.from_dict(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_additional_properties_inlined.additional_properties = additional_properties_dict return model_with_additional_properties_inlined @property 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 index 7c9a86cb4..a689e8d6e 100644 --- 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 @@ -14,7 +14,9 @@ class ModelWithAdditionalPropertiesRefed: def to_dict(self) -> Dict[str, Any]: field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) + for prop_name, prop in self.additional_properties.items(): + field_dict[prop_name] = prop.value + field_dict.update({}) return field_dict @@ -24,7 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - model_with_additional_properties_refed.additional_properties = d + additional_properties_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = AnEnum(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_additional_properties_refed.additional_properties = additional_properties_dict return model_with_additional_properties_refed @property 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..4b327a446 --- /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_dict = {} + for prop_name, prop_dict in d.items(): + additional_properties = isoparse(prop_dict) + + additional_properties_dict[prop_name] = additional_properties + + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties_dict + 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 From 0d175c0ac9f1b3dfe94d1870f7c3d3986adb3a05 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 2 Dec 2020 19:17:34 -0800 Subject: [PATCH 12/20] Update templates to not attempt to pop the same attr multiple times --- .../templates/property_templates/date_property.pyi | 5 +++-- .../templates/property_templates/datetime_property.pyi | 5 +++-- .../templates/property_templates/dict_property.pyi | 5 +++-- .../templates/property_templates/enum_property.pyi | 5 +++-- .../templates/property_templates/list_property.pyi | 5 +++-- 5 files changed, 15 insertions(+), 10 deletions(-) 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) }} From 1f38a05a5e5cd37c80649f5ce63b3da8cacc7101 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Wed, 2 Dec 2020 19:18:05 -0800 Subject: [PATCH 13/20] Update golden record --- .../custom_e2e/api/tests/get_user_list.py | 3 ++- .../golden-record-custom/custom_e2e/models/a_model.py | 6 ++++-- .../custom_e2e/models/http_validation_error.py | 3 ++- .../custom_e2e/models/model_with_union_property.py | 10 ++++++---- .../my_test_api_client/api/tests/get_user_list.py | 3 ++- .../golden-record/my_test_api_client/models/a_model.py | 6 ++++-- .../my_test_api_client/models/http_validation_error.py | 3 ++- .../models/model_with_union_property.py | 10 ++++++---- 8 files changed, 28 insertions(+), 16 deletions(-) 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/a_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py index 6bfca16ff..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 @@ -98,9 +98,11 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") nested_list_of_enums = [] - for nested_list_of_enums_item_data in d.pop("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) 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 e9d4ea11c..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 @@ -32,7 +32,8 @@ def to_dict(self) -> Dict[str, Any]: def from_dict(src_dict: Dict[str, Any]) -> "HTTPValidationError": d = src_dict.copy() detail = [] - for detail_item_data in d.pop("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) 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 d9508293f..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 @@ -43,15 +43,17 @@ def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: 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 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/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index 6bfca16ff..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 @@ -98,9 +98,11 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") nested_list_of_enums = [] - for nested_list_of_enums_item_data in d.pop("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) 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 e9d4ea11c..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 @@ -32,7 +32,8 @@ def to_dict(self) -> Dict[str, Any]: def from_dict(src_dict: Dict[str, Any]) -> "HTTPValidationError": d = src_dict.copy() detail = [] - for detail_item_data in d.pop("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) 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 d9508293f..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 @@ -43,15 +43,17 @@ def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: 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 From da0bc8ee84ba1262f895323dcbe196326157fe8b Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 12:54:40 -0800 Subject: [PATCH 14/20] Typing fixes for additional_properties when building model prop --- openapi_python_client/parser/properties/__init__.py | 1 + openapi_python_client/parser/properties/model_property.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index fd7b57c30..bc888fac6 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -255,6 +255,7 @@ 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): diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index dfad72aa9..82f670a7d 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -16,7 +16,7 @@ class ModelProperty(Property): optional_properties: List[Property] description: str relative_imports: Set[str] - additional_properties: Optional[Union[bool, Property]] + additional_properties: Union[bool, Property] template: ClassVar[str] = "model_property.pyi" From 827d36767d41d8dd69792cbc88d204ec6ab778c6 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 12:57:23 -0800 Subject: [PATCH 15/20] Remove unused import from model_property --- openapi_python_client/parser/properties/model_property.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 82f670a7d..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, Optional, Set, Union +from typing import ClassVar, List, Set, Union import attr From daf96281c7a3fada82af9696849e3ef151e20061 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 15:42:03 -0800 Subject: [PATCH 16/20] Update existing tests to account for additional_properties --- tests/test_parser/test_properties/test_init.py | 4 ++++ tests/test_parser/test_properties/test_model_property.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 96047c60f..e8777c234 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 @@ -1051,6 +1053,7 @@ def test_build_model_property(): }, description="A class called MyModel", nullable=False, + additional_properties=oai.Schema.construct(), ) schemas = Schemas(models={"OtherModel": None}) @@ -1083,6 +1086,7 @@ def test_build_model_property(): "from ..types import UNSET, Unset", "from typing import Union", }, + additional_properties=True, ) 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="..") == { From 036f3720cccb3745cfe4e1cee91a4cce34f3db8b Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 16:12:40 -0800 Subject: [PATCH 17/20] Parametrize test_build_model_property to test various additional_properties values --- .../test_parser/test_properties/test_init.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index e8777c234..cc3a2b06f 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -1041,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="AdditionalProperties", 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( @@ -1053,7 +1066,7 @@ def test_build_model_property(): }, description="A class called MyModel", nullable=False, - additional_properties=oai.Schema.construct(), + additionalProperties=additional_properties_schema, ) schemas = Schemas(models={"OtherModel": None}) @@ -1086,7 +1099,7 @@ def test_build_model_property(): "from ..types import UNSET, Unset", "from typing import Union", }, - additional_properties=True, + additional_properties=expected_additional_properties, ) From e1ef9a796b9564ef687c2e3772a57bb61c92db76 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 16:16:12 -0800 Subject: [PATCH 18/20] Rename AdditionalProperties models to AdditionalProperty as each one is singular --- .../custom_e2e/models/__init__.py | 4 ++-- ...model_with_additional_properties_inlined.py | 18 +++++++++--------- ..._properties_inlined_additional_property.py} | 12 ++++++------ .../model_with_additional_properties_refed.py | 8 ++++---- ...tive_additional_properties_a_date_holder.py | 8 ++++---- .../my_test_api_client/models/__init__.py | 4 ++-- ...model_with_additional_properties_inlined.py | 18 +++++++++--------- ..._properties_inlined_additional_property.py} | 12 ++++++------ .../model_with_additional_properties_refed.py | 8 ++++---- ...tive_additional_properties_a_date_holder.py | 8 ++++---- .../parser/properties/__init__.py | 2 +- openapi_python_client/templates/model.pyi | 6 +++--- tests/test_parser/test_properties/test_init.py | 2 +- 13 files changed, 55 insertions(+), 55 deletions(-) rename end_to_end_tests/golden-record-custom/custom_e2e/models/{model_with_additional_properties_inlined_additional_properties.py => model_with_additional_properties_inlined_additional_property.py} (90%) rename end_to_end_tests/golden-record/my_test_api_client/models/{model_with_additional_properties_inlined_additional_properties.py => model_with_additional_properties_inlined_additional_property.py} (90%) 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 939c56666..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 @@ -8,8 +8,8 @@ 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_properties import ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties, +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 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 index 4abf1bb24..30acb7957 100644 --- 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 @@ -2,8 +2,8 @@ import attr -from ..models.model_with_additional_properties_inlined_additional_properties import ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties, +from ..models.model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, ) from ..types import UNSET, Unset @@ -13,7 +13,7 @@ class ModelWithAdditionalPropertiesInlined: """ """ a_number: Union[Unset, float] = UNSET - additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = attr.ib( init=False, factory=dict ) @@ -39,23 +39,23 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = ModelWithAdditionalPropertiesInlinedAdditionalProperties.from_dict(prop_dict) + additional_property = ModelWithAdditionalPropertiesInlinedAdditionalProperty.from_dict(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_additional_properties_inlined.additional_properties = additional_properties_dict + 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) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty: return self.additional_properties[key] - def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> None: + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py similarity index 90% rename from end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py rename to end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py index 388dddc54..4ce45f8ef 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_properties.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_additional_properties_inlined_additional_property.py @@ -6,7 +6,7 @@ @attr.s(auto_attribs=True) -class ModelWithAdditionalPropertiesInlinedAdditionalProperties: +class ModelWithAdditionalPropertiesInlinedAdditionalProperty: """ """ extra_props_prop: Union[Unset, str] = UNSET @@ -24,18 +24,18 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperties": + 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_properties = ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties( + model_with_additional_properties_inlined_additional_property = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty( extra_props_prop=extra_props_prop, ) ) - model_with_additional_properties_inlined_additional_properties.additional_properties = d - return model_with_additional_properties_inlined_additional_properties + 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]: 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 index a689e8d6e..c5eb4a7a4 100644 --- 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 @@ -26,13 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = AnEnum(prop_dict) + additional_property = AnEnum(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_additional_properties_refed.additional_properties = additional_properties_dict + model_with_additional_properties_refed.additional_properties = additional_properties return model_with_additional_properties_refed @property 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 index 4b327a446..394c68a12 100644 --- 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 @@ -26,13 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperti d = src_dict.copy() model_with_primitive_additional_properties_a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder() - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = isoparse(prop_dict) + additional_property = isoparse(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties_dict + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties return model_with_primitive_additional_properties_a_date_holder @property 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 939c56666..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 @@ -8,8 +8,8 @@ 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_properties import ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties, +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 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 index 4abf1bb24..30acb7957 100644 --- 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 @@ -2,8 +2,8 @@ import attr -from ..models.model_with_additional_properties_inlined_additional_properties import ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties, +from ..models.model_with_additional_properties_inlined_additional_property import ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty, ) from ..types import UNSET, Unset @@ -13,7 +13,7 @@ class ModelWithAdditionalPropertiesInlined: """ """ a_number: Union[Unset, float] = UNSET - additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperties] = attr.ib( + additional_properties: Dict[str, ModelWithAdditionalPropertiesInlinedAdditionalProperty] = attr.ib( init=False, factory=dict ) @@ -39,23 +39,23 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlined a_number=a_number, ) - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = ModelWithAdditionalPropertiesInlinedAdditionalProperties.from_dict(prop_dict) + additional_property = ModelWithAdditionalPropertiesInlinedAdditionalProperty.from_dict(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_additional_properties_inlined.additional_properties = additional_properties_dict + 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) -> ModelWithAdditionalPropertiesInlinedAdditionalProperties: + def __getitem__(self, key: str) -> ModelWithAdditionalPropertiesInlinedAdditionalProperty: return self.additional_properties[key] - def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperties) -> None: + def __setitem__(self, key: str, value: ModelWithAdditionalPropertiesInlinedAdditionalProperty) -> None: self.additional_properties[key] = value def __delitem__(self, key: str) -> None: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py similarity index 90% rename from end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py rename to end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py index 388dddc54..4ce45f8ef 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_additional_properties_inlined_additional_property.py @@ -6,7 +6,7 @@ @attr.s(auto_attribs=True) -class ModelWithAdditionalPropertiesInlinedAdditionalProperties: +class ModelWithAdditionalPropertiesInlinedAdditionalProperty: """ """ extra_props_prop: Union[Unset, str] = UNSET @@ -24,18 +24,18 @@ def to_dict(self) -> Dict[str, Any]: return field_dict @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesInlinedAdditionalProperties": + 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_properties = ( - ModelWithAdditionalPropertiesInlinedAdditionalProperties( + model_with_additional_properties_inlined_additional_property = ( + ModelWithAdditionalPropertiesInlinedAdditionalProperty( extra_props_prop=extra_props_prop, ) ) - model_with_additional_properties_inlined_additional_properties.additional_properties = d - return model_with_additional_properties_inlined_additional_properties + 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]: 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 index a689e8d6e..c5eb4a7a4 100644 --- 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 @@ -26,13 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithAdditionalPropertiesRefed": d = src_dict.copy() model_with_additional_properties_refed = ModelWithAdditionalPropertiesRefed() - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = AnEnum(prop_dict) + additional_property = AnEnum(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_additional_properties_refed.additional_properties = additional_properties_dict + model_with_additional_properties_refed.additional_properties = additional_properties return model_with_additional_properties_refed @property 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 index 4b327a446..394c68a12 100644 --- 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 @@ -26,13 +26,13 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperti d = src_dict.copy() model_with_primitive_additional_properties_a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder() - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): - additional_properties = isoparse(prop_dict) + additional_property = isoparse(prop_dict) - additional_properties_dict[prop_name] = additional_properties + additional_properties[prop_name] = additional_property - model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties_dict + model_with_primitive_additional_properties_a_date_holder.additional_properties = additional_properties return model_with_primitive_additional_properties_a_date_holder @property diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index bc888fac6..98fc4fc14 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -266,7 +266,7 @@ def build_model_property( else: assert isinstance(data.additionalProperties, (oai.Schema, oai.Reference)) additional_properties, schemas = property_from_data( - name="AdditionalProperties", + name="AdditionalProperty", required=True, # in the sense that if present in the dict will not be None data=data.additionalProperties, schemas=schemas, diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index e64b6f8ae..c66ba46a3 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -98,12 +98,12 @@ class {{ model.reference.class_name }}: {% if model.additional_properties %} {% if model.additional_properties.template %} {% from "property_templates/" + model.additional_properties.template import construct %} - additional_properties_dict = {} + additional_properties = {} for prop_name, prop_dict in d.items(): {{ construct(model.additional_properties, "prop_dict") | indent(12) }} - additional_properties_dict[prop_name] = {{ model.additional_properties.python_name }} + additional_properties[prop_name] = {{ model.additional_properties.python_name }} - {{model.reference.module_name}}.additional_properties = additional_properties_dict + {{model.reference.module_name}}.additional_properties = additional_properties {% else %} {{model.reference.module_name}}.additional_properties = d {% endif %} diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index cc3a2b06f..c53241101 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -1050,7 +1050,7 @@ def test_build_enums(mocker): (False, False), ( oai.Schema.construct(type="string"), - StringProperty(name="AdditionalProperties", required=True, nullable=False, default=None), + StringProperty(name="AdditionalProperty", required=True, nullable=False, default=None), ), ], ) From 0cbe2de7c878e7d423f99018bbc80051dc74a17a Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 16:28:12 -0800 Subject: [PATCH 19/20] Add test for bad additionalProperties schema --- .../test_parser/test_properties/test_init.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index c53241101..88690c9da 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -1125,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 From 6ccdde4b723573f10d52ddec8c2c686fe66a3200 Mon Sep 17 00:00:00 2001 From: Packy Gallagher Date: Thu, 3 Dec 2020 16:28:48 -0800 Subject: [PATCH 20/20] uncomment part of test --- tests/test_parser/test_properties/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 88690c9da..b2dd1bb2f 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -1145,7 +1145,7 @@ def test_build_model_property_bad_additional_props(): parent_name=None, ) - # assert new_schemas == schemas + assert new_schemas == schemas assert err == PropertyError(detail="unknown type not_real", data=oai.Schema(type="not_real"))