From 7bb78327162f6588ac0bf074b9fefea7ac28d118 Mon Sep 17 00:00:00 2001 From: Karl Gutwin Date: Wed, 3 Jan 2024 11:07:42 -0500 Subject: [PATCH 1/4] Add end-to-end tests and proposed fixes to nullable array support --- end_to_end_tests/baseline_openapi_3.0.json | 3 +- end_to_end_tests/baseline_openapi_3.1.yaml | 4 +- .../body_upload_file_tests_upload_post.py | 60 ++++++++++++++++--- .../parser/properties/list_property.py | 27 +++++++++ .../property_templates/list_property.py.jinja | 3 + 5 files changed, 85 insertions(+), 12 deletions(-) diff --git a/end_to_end_tests/baseline_openapi_3.0.json b/end_to_end_tests/baseline_openapi_3.0.json index d21d1d57f..f9a33e5c9 100644 --- a/end_to_end_tests/baseline_openapi_3.0.json +++ b/end_to_end_tests/baseline_openapi_3.0.json @@ -1778,9 +1778,10 @@ }, "some_array": { "title": "Some Array", + "nullable": true, "type": "array", "items": { - "type": "number" + "$ref": "#/components/schemas/AFormData" } }, "some_object": { diff --git a/end_to_end_tests/baseline_openapi_3.1.yaml b/end_to_end_tests/baseline_openapi_3.1.yaml index 03270af6b..78fa7fb59 100644 --- a/end_to_end_tests/baseline_openapi_3.1.yaml +++ b/end_to_end_tests/baseline_openapi_3.1.yaml @@ -1794,9 +1794,9 @@ info: }, "some_array": { "title": "Some Array", - "type": "array", + "type": [ "array", "null" ], "items": { - "type": "number" + "$ref": "#/components/schemas/AFormData" } }, "some_object": { 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 bdd28ab40..cf2e5c206 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 @@ -11,6 +11,7 @@ from ..types import UNSET, File, FileJsonType, Unset if TYPE_CHECKING: + from ..models.a_form_data import AFormData from ..models.body_upload_file_tests_upload_post_additional_property import ( BodyUploadFileTestsUploadPostAdditionalProperty, ) @@ -38,7 +39,7 @@ class BodyUploadFileTestsUploadPost: a_datetime (Union[Unset, datetime.datetime]): a_date (Union[Unset, datetime.date]): some_number (Union[Unset, float]): - some_array (Union[Unset, List[float]]): + some_array (Union[List['AFormData'], None, Unset]): some_optional_object (Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject]): some_enum (Union[Unset, DifferentEnum]): An enumeration. """ @@ -51,7 +52,7 @@ class BodyUploadFileTestsUploadPost: a_datetime: Union[Unset, datetime.datetime] = UNSET a_date: Union[Unset, datetime.date] = UNSET some_number: Union[Unset, float] = UNSET - some_array: Union[Unset, List[float]] = UNSET + some_array: Union[List["AFormData"], None, Unset] = UNSET some_optional_object: Union[Unset, "BodyUploadFileTestsUploadPostSomeOptionalObject"] = UNSET some_enum: Union[Unset, DifferentEnum] = UNSET additional_properties: Dict[str, "BodyUploadFileTestsUploadPostAdditionalProperty"] = _attrs_field( @@ -89,8 +90,18 @@ def to_dict(self) -> Dict[str, Any]: some_number = self.some_number - some_array: Union[Unset, List[float]] = UNSET - if not isinstance(self.some_array, Unset): + some_array: Union[List[Dict[str, Any]], None, Unset] + if isinstance(self.some_array, Unset): + some_array = UNSET + elif isinstance(self.some_array, list): + some_array = UNSET + if not isinstance(self.some_array, Unset): + some_array = [] + for some_array_type_0_item_data in self.some_array: + some_array_type_0_item = some_array_type_0_item_data.to_dict() + some_array.append(some_array_type_0_item) + + else: some_array = self.some_array some_optional_object: Union[Unset, Dict[str, Any]] = UNSET @@ -165,10 +176,20 @@ def to_multipart(self) -> Dict[str, Any]: else (None, str(self.some_number).encode(), "text/plain") ) - some_array: Union[Unset, Tuple[None, bytes, str]] = UNSET - if not isinstance(self.some_array, Unset): - _temp_some_array = self.some_array - some_array = (None, json.dumps(_temp_some_array).encode(), "application/json") + some_array: Union[None, Tuple[None, bytes, str], Unset] + if isinstance(self.some_array, Unset): + some_array = UNSET + elif isinstance(self.some_array, list): + some_array = UNSET + if not isinstance(self.some_array, Unset): + _temp_some_array = [] + for some_array_type_0_item_data in self.some_array: + some_array_type_0_item = some_array_type_0_item_data.to_dict() + _temp_some_array.append(some_array_type_0_item) + some_array = (None, json.dumps(_temp_some_array).encode(), "application/json") + + else: + some_array = self.some_array some_optional_object: Union[Unset, Tuple[None, bytes, str]] = UNSET if not isinstance(self.some_optional_object, Unset): @@ -209,6 +230,7 @@ def to_multipart(self) -> Dict[str, Any]: @classmethod def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + from ..models.a_form_data import AFormData from ..models.body_upload_file_tests_upload_post_additional_property import ( BodyUploadFileTestsUploadPostAdditionalProperty, ) @@ -265,7 +287,27 @@ def _parse_some_nullable_object(data: object) -> Union["BodyUploadFileTestsUploa some_number = d.pop("some_number", UNSET) - some_array = cast(List[float], d.pop("some_array", UNSET)) + def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, list): + raise TypeError() + some_array_type_0 = [] + _some_array_type_0 = data + for some_array_type_0_item_data in _some_array_type_0 or []: + some_array_type_0_item = AFormData.from_dict(some_array_type_0_item_data) + + some_array_type_0.append(some_array_type_0_item) + + return some_array_type_0 + except: # noqa: E722 + pass + return cast(Union[List["AFormData"], None, Unset], data) + + some_array = _parse_some_array(d.pop("some_array", UNSET)) _some_optional_object = d.pop("some_optional_object", UNSET) some_optional_object: Union[Unset, BodyUploadFileTestsUploadPostSomeOptionalObject] diff --git a/openapi_python_client/parser/properties/list_property.py b/openapi_python_client/parser/properties/list_property.py index 7adc1f682..b33e6e45f 100644 --- a/openapi_python_client/parser/properties/list_property.py +++ b/openapi_python_client/parser/properties/list_property.py @@ -116,3 +116,30 @@ def get_lazy_imports(self, *, prefix: str) -> set[str]: lazy_imports = super().get_lazy_imports(prefix=prefix) lazy_imports.update(self.inner_property.get_lazy_imports(prefix=prefix)) return lazy_imports + + def get_type_string( + self, + no_optional: bool = False, + json: bool = False, + *, + multipart: bool = False, + quoted: bool = False, + ) -> str: + """ + Get a string representation of type that should be used when declaring this property + + Args: + no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks) + json: True if the type refers to the property after JSON serialization + """ + if json: + type_string = self.get_base_json_type_string() + elif multipart: + type_string = "Tuple[None, bytes, str]" + else: + type_string = self.get_base_type_string() + + if no_optional or self.required: + return type_string + return f"Union[Unset, {type_string}]" + diff --git a/openapi_python_client/templates/property_templates/list_property.py.jinja b/openapi_python_client/templates/property_templates/list_property.py.jinja index 075160cf9..8c7e1e78a 100644 --- a/openapi_python_client/templates/property_templates/list_property.py.jinja +++ b/openapi_python_client/templates/property_templates/list_property.py.jinja @@ -3,6 +3,9 @@ {% import "property_templates/" + inner_property.template as inner_template %} {% if inner_template.construct %} {% set inner_source = inner_property.python_name + "_data" %} +{% if initial_value == "UNSET" %} +{% set initial_value = "[]" %} +{% endif %} {{ property.python_name }} = {{ initial_value }} _{{ property.python_name }} = {{ source }} {% if property.required %} From 6c4c5b34d6a599ddb94384d156c85a1a3c13961d Mon Sep 17 00:00:00 2001 From: Karl Gutwin Date: Wed, 3 Jan 2024 11:14:29 -0500 Subject: [PATCH 2/4] missed ruff reformat --- openapi_python_client/parser/properties/list_property.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi_python_client/parser/properties/list_property.py b/openapi_python_client/parser/properties/list_property.py index b33e6e45f..c78e50513 100644 --- a/openapi_python_client/parser/properties/list_property.py +++ b/openapi_python_client/parser/properties/list_property.py @@ -142,4 +142,3 @@ def get_type_string( if no_optional or self.required: return type_string return f"Union[Unset, {type_string}]" - From 9ac26786a245a6c33443fc04f928c6266002e494 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Wed, 3 Jan 2024 17:03:50 -0700 Subject: [PATCH 3/4] Clean up templates and document changes --- .changeset/fix_lists_within_unions.md | 9 +++ ...ify_type_checks_for_non_required_unions.md | 5 ++ .../defaults/defaults_tests_defaults_post.py | 5 +- .../get_location_query_optionality.py | 4 +- .../my_test_api_client/models/a_model.py | 61 +++---------------- .../body_upload_file_tests_upload_post.py | 24 +++----- .../models/model_with_union_property.py | 23 ++----- .../model_with_union_property_inlined.py | 22 ++----- .../parser/properties/union.py | 2 +- .../property_templates/date_property.py.jinja | 4 +- .../datetime_property.py.jinja | 4 +- .../property_templates/enum_property.py.jinja | 4 +- .../property_templates/file_property.py.jinja | 4 +- .../property_templates/list_property.py.jinja | 7 +-- .../model_property.py.jinja | 4 +- .../property_macros.py.jinja | 5 +- .../union_property.py.jinja | 6 +- 17 files changed, 62 insertions(+), 131 deletions(-) create mode 100644 .changeset/fix_lists_within_unions.md create mode 100644 .changeset/simplify_type_checks_for_non_required_unions.md diff --git a/.changeset/fix_lists_within_unions.md b/.changeset/fix_lists_within_unions.md new file mode 100644 index 000000000..d119fad4b --- /dev/null +++ b/.changeset/fix_lists_within_unions.md @@ -0,0 +1,9 @@ +--- +default: patch +--- + +# Fix lists within unions + +Fixes #756 and #928. Arrays within unions (which, as of 0.17 includes nullable arrays) would generate invalid code. + +Thanks @kgutwin and @diesieben07! diff --git a/.changeset/simplify_type_checks_for_non_required_unions.md b/.changeset/simplify_type_checks_for_non_required_unions.md new file mode 100644 index 000000000..2bfaee23a --- /dev/null +++ b/.changeset/simplify_type_checks_for_non_required_unions.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +# Simplify type checks for non-required unions diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py index 02f5b6ff7..a14cb2670 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/defaults/defaults_tests_defaults_post.py @@ -58,10 +58,7 @@ def _get_kwargs( if isinstance(union_prop_with_ref, Unset): json_union_prop_with_ref = UNSET elif isinstance(union_prop_with_ref, AnEnum): - json_union_prop_with_ref = UNSET - if not isinstance(union_prop_with_ref, Unset): - json_union_prop_with_ref = union_prop_with_ref.value - + json_union_prop_with_ref = union_prop_with_ref.value else: json_union_prop_with_ref = union_prop_with_ref params["union_prop_with_ref"] = json_union_prop_with_ref diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py index c50642749..ea43b6731 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/location/get_location_query_optionality.py @@ -32,9 +32,7 @@ def _get_kwargs( if isinstance(null_not_required, Unset): json_null_not_required = UNSET elif isinstance(null_not_required, datetime.datetime): - json_null_not_required = UNSET - if not isinstance(null_not_required, Unset): - json_null_not_required = null_not_required.isoformat() + json_null_not_required = null_not_required.isoformat() else: json_null_not_required = null_not_required params["null_not_required"] = json_null_not_required 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 f1bd543ce..d14160bf8 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 @@ -159,25 +159,17 @@ def to_dict(self) -> Dict[str, Any]: if isinstance(self.not_required_one_of_models, Unset): not_required_one_of_models = UNSET elif isinstance(self.not_required_one_of_models, FreeFormModel): - not_required_one_of_models = UNSET - if not isinstance(self.not_required_one_of_models, Unset): - not_required_one_of_models = self.not_required_one_of_models.to_dict() + not_required_one_of_models = self.not_required_one_of_models.to_dict() else: - not_required_one_of_models = UNSET - if not isinstance(self.not_required_one_of_models, Unset): - not_required_one_of_models = self.not_required_one_of_models.to_dict() + not_required_one_of_models = self.not_required_one_of_models.to_dict() not_required_nullable_one_of_models: Union[Dict[str, Any], None, Unset, str] if isinstance(self.not_required_nullable_one_of_models, Unset): not_required_nullable_one_of_models = UNSET elif isinstance(self.not_required_nullable_one_of_models, FreeFormModel): - not_required_nullable_one_of_models = UNSET - if not isinstance(self.not_required_nullable_one_of_models, Unset): - not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict() + not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict() elif isinstance(self.not_required_nullable_one_of_models, ModelWithUnionProperty): - not_required_nullable_one_of_models = UNSET - if not isinstance(self.not_required_nullable_one_of_models, Unset): - not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict() + not_required_nullable_one_of_models = self.not_required_nullable_one_of_models.to_dict() else: not_required_nullable_one_of_models = self.not_required_nullable_one_of_models @@ -189,9 +181,7 @@ def to_dict(self) -> Dict[str, Any]: if isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = UNSET elif isinstance(self.not_required_nullable_model, ModelWithUnionProperty): - not_required_nullable_model = UNSET - if not isinstance(self.not_required_nullable_model, Unset): - not_required_nullable_model = self.not_required_nullable_model.to_dict() + not_required_nullable_model = self.not_required_nullable_model.to_dict() else: not_required_nullable_model = self.not_required_nullable_model @@ -401,24 +391,14 @@ def _parse_not_required_one_of_models(data: object) -> Union["FreeFormModel", "M try: if not isinstance(data, dict): raise TypeError() - _not_required_one_of_models_type_0 = data - not_required_one_of_models_type_0: Union[Unset, FreeFormModel] - if isinstance(_not_required_one_of_models_type_0, Unset): - not_required_one_of_models_type_0 = UNSET - else: - not_required_one_of_models_type_0 = FreeFormModel.from_dict(_not_required_one_of_models_type_0) + not_required_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_one_of_models_type_0 except: # noqa: E722 pass if not isinstance(data, dict): raise TypeError() - _not_required_one_of_models_type_1 = data - not_required_one_of_models_type_1: Union[Unset, ModelWithUnionProperty] - if isinstance(_not_required_one_of_models_type_1, Unset): - not_required_one_of_models_type_1 = UNSET - else: - not_required_one_of_models_type_1 = ModelWithUnionProperty.from_dict(_not_required_one_of_models_type_1) + not_required_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_one_of_models_type_1 @@ -434,14 +414,7 @@ def _parse_not_required_nullable_one_of_models( try: if not isinstance(data, dict): raise TypeError() - _not_required_nullable_one_of_models_type_0 = data - not_required_nullable_one_of_models_type_0: Union[Unset, FreeFormModel] - if isinstance(_not_required_nullable_one_of_models_type_0, Unset): - not_required_nullable_one_of_models_type_0 = UNSET - else: - not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict( - _not_required_nullable_one_of_models_type_0 - ) + not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(data) return not_required_nullable_one_of_models_type_0 except: # noqa: E722 @@ -449,14 +422,7 @@ def _parse_not_required_nullable_one_of_models( try: if not isinstance(data, dict): raise TypeError() - _not_required_nullable_one_of_models_type_1 = data - not_required_nullable_one_of_models_type_1: Union[Unset, ModelWithUnionProperty] - if isinstance(_not_required_nullable_one_of_models_type_1, Unset): - not_required_nullable_one_of_models_type_1 = UNSET - else: - not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict( - _not_required_nullable_one_of_models_type_1 - ) + not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_one_of_models_type_1 except: # noqa: E722 @@ -482,14 +448,7 @@ def _parse_not_required_nullable_model(data: object) -> Union["ModelWithUnionPro try: if not isinstance(data, dict): raise TypeError() - _not_required_nullable_model_type_1 = data - not_required_nullable_model_type_1: Union[Unset, ModelWithUnionProperty] - if isinstance(_not_required_nullable_model_type_1, Unset): - not_required_nullable_model_type_1 = UNSET - else: - not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict( - _not_required_nullable_model_type_1 - ) + not_required_nullable_model_type_1 = ModelWithUnionProperty.from_dict(data) return not_required_nullable_model_type_1 except: # noqa: E722 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 cf2e5c206..20d27d4a6 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 @@ -94,12 +94,10 @@ def to_dict(self) -> Dict[str, Any]: if isinstance(self.some_array, Unset): some_array = UNSET elif isinstance(self.some_array, list): - some_array = UNSET - if not isinstance(self.some_array, Unset): - some_array = [] - for some_array_type_0_item_data in self.some_array: - some_array_type_0_item = some_array_type_0_item_data.to_dict() - some_array.append(some_array_type_0_item) + some_array = [] + for some_array_type_0_item_data in self.some_array: + some_array_type_0_item = some_array_type_0_item_data.to_dict() + some_array.append(some_array_type_0_item) else: some_array = self.some_array @@ -180,13 +178,11 @@ def to_multipart(self) -> Dict[str, Any]: if isinstance(self.some_array, Unset): some_array = UNSET elif isinstance(self.some_array, list): - some_array = UNSET - if not isinstance(self.some_array, Unset): - _temp_some_array = [] - for some_array_type_0_item_data in self.some_array: - some_array_type_0_item = some_array_type_0_item_data.to_dict() - _temp_some_array.append(some_array_type_0_item) - some_array = (None, json.dumps(_temp_some_array).encode(), "application/json") + _temp_some_array = [] + for some_array_type_0_item_data in self.some_array: + some_array_type_0_item = some_array_type_0_item_data.to_dict() + _temp_some_array.append(some_array_type_0_item) + some_array = (None, json.dumps(_temp_some_array).encode(), "application/json") else: some_array = self.some_array @@ -297,7 +293,7 @@ def _parse_some_array(data: object) -> Union[List["AFormData"], None, Unset]: raise TypeError() some_array_type_0 = [] _some_array_type_0 = data - for some_array_type_0_item_data in _some_array_type_0 or []: + for some_array_type_0_item_data in _some_array_type_0: some_array_type_0_item = AFormData.from_dict(some_array_type_0_item_data) some_array_type_0.append(some_array_type_0_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 916de9079..890010b78 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 @@ -23,14 +23,9 @@ def to_dict(self) -> Dict[str, Any]: if isinstance(self.a_property, Unset): a_property = UNSET elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property.value - + a_property = self.a_property.value else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property.value + a_property = self.a_property.value field_dict: Dict[str, Any] = {} field_dict.update({}) @@ -49,24 +44,14 @@ def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: try: if not isinstance(data, str): raise TypeError() - _a_property_type_0 = data - a_property_type_0: Union[Unset, AnEnum] - if isinstance(_a_property_type_0, Unset): - a_property_type_0 = UNSET - else: - a_property_type_0 = AnEnum(_a_property_type_0) + a_property_type_0 = AnEnum(data) return a_property_type_0 except: # noqa: E722 pass if not isinstance(data, int): raise TypeError() - _a_property_type_1 = data - a_property_type_1: Union[Unset, AnIntEnum] - if isinstance(_a_property_type_1, Unset): - a_property_type_1 = UNSET - else: - a_property_type_1 = AnIntEnum(_a_property_type_1) + a_property_type_1 = AnIntEnum(data) return a_property_type_1 diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py index 2ccfe5e1b..2a832e21a 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py @@ -28,13 +28,9 @@ def to_dict(self) -> Dict[str, Any]: if isinstance(self.fruit, Unset): fruit = UNSET elif isinstance(self.fruit, ModelWithUnionPropertyInlinedFruitType0): - fruit = UNSET - if not isinstance(self.fruit, Unset): - fruit = self.fruit.to_dict() + fruit = self.fruit.to_dict() else: - fruit = UNSET - if not isinstance(self.fruit, Unset): - fruit = self.fruit.to_dict() + fruit = self.fruit.to_dict() field_dict: Dict[str, Any] = {} field_dict.update({}) @@ -58,24 +54,14 @@ def _parse_fruit( try: if not isinstance(data, dict): raise TypeError() - _fruit_type_0 = data - fruit_type_0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0] - if isinstance(_fruit_type_0, Unset): - fruit_type_0 = UNSET - else: - fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(_fruit_type_0) + fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(data) return fruit_type_0 except: # noqa: E722 pass if not isinstance(data, dict): raise TypeError() - _fruit_type_1 = data - fruit_type_1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1] - if isinstance(_fruit_type_1, Unset): - fruit_type_1 = UNSET - else: - fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(_fruit_type_1) + fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(data) return fruit_type_1 diff --git a/openapi_python_client/parser/properties/union.py b/openapi_python_client/parser/properties/union.py index a18ad24d2..4ec581be1 100644 --- a/openapi_python_client/parser/properties/union.py +++ b/openapi_python_client/parser/properties/union.py @@ -57,7 +57,7 @@ def build( for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf, type_list_data)): sub_prop, schemas = property_from_data( name=f"{name}_type_{i}", - required=required, + required=True, data=sub_prop_data, schemas=schemas, parent_name=parent_name, diff --git a/openapi_python_client/templates/property_templates/date_property.py.jinja b/openapi_python_client/templates/property_templates/date_property.py.jinja index c1f8b668f..5a3fdeafc 100644 --- a/openapi_python_client/templates/property_templates/date_property.py.jinja +++ b/openapi_python_client/templates/property_templates/date_property.py.jinja @@ -4,8 +4,8 @@ isoparse({{ source }}).date() {% from "property_templates/property_macros.py.jinja" import construct_template %} -{% macro construct(property, source, initial_value=None) %} -{{ construct_template(construct_function, property, source, initial_value=initial_value) }} +{% macro construct(property, source) %} +{{ construct_template(construct_function, property, source) }} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} diff --git a/openapi_python_client/templates/property_templates/datetime_property.py.jinja b/openapi_python_client/templates/property_templates/datetime_property.py.jinja index 18673087e..2ff54f4dc 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.py.jinja +++ b/openapi_python_client/templates/property_templates/datetime_property.py.jinja @@ -4,8 +4,8 @@ isoparse({{ source }}) {% from "property_templates/property_macros.py.jinja" import construct_template %} -{% macro construct(property, source, initial_value=None) %} -{{ construct_template(construct_function, property, source, initial_value=initial_value) }} +{% macro construct(property, source) %} +{{ construct_template(construct_function, property, source) }} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} diff --git a/openapi_python_client/templates/property_templates/enum_property.py.jinja b/openapi_python_client/templates/property_templates/enum_property.py.jinja index 08744ee90..d01137f03 100644 --- a/openapi_python_client/templates/property_templates/enum_property.py.jinja +++ b/openapi_python_client/templates/property_templates/enum_property.py.jinja @@ -4,8 +4,8 @@ {% from "property_templates/property_macros.py.jinja" import construct_template %} -{% macro construct(property, source, initial_value=None) %} -{{ construct_template(construct_function, property, source, initial_value=initial_value) }} +{% macro construct(property, source) %} +{{ construct_template(construct_function, property, source) }} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.value_type.__name__ }}){% endmacro %} diff --git a/openapi_python_client/templates/property_templates/file_property.py.jinja b/openapi_python_client/templates/property_templates/file_property.py.jinja index f9d6f3ed1..c19a068c5 100644 --- a/openapi_python_client/templates/property_templates/file_property.py.jinja +++ b/openapi_python_client/templates/property_templates/file_property.py.jinja @@ -6,8 +6,8 @@ File( {% from "property_templates/property_macros.py.jinja" import construct_template %} -{% macro construct(property, source, initial_value=None) %} -{{ construct_template(construct_function, property, source, initial_value=initial_value) }} +{% macro construct(property, source) %} +{{ construct_template(construct_function, property, source) }} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, bytes){% endmacro %} diff --git a/openapi_python_client/templates/property_templates/list_property.py.jinja b/openapi_python_client/templates/property_templates/list_property.py.jinja index 8c7e1e78a..0e5d1b5e3 100644 --- a/openapi_python_client/templates/property_templates/list_property.py.jinja +++ b/openapi_python_client/templates/property_templates/list_property.py.jinja @@ -1,12 +1,9 @@ -{% macro construct(property, source, initial_value="[]") %} +{% macro construct(property, source) %} {% set inner_property = property.inner_property %} {% import "property_templates/" + inner_property.template as inner_template %} {% if inner_template.construct %} {% set inner_source = inner_property.python_name + "_data" %} -{% if initial_value == "UNSET" %} -{% set initial_value = "[]" %} -{% endif %} -{{ property.python_name }} = {{ initial_value }} +{{ property.python_name }} = [] _{{ property.python_name }} = {{ source }} {% if property.required %} for {{ inner_source }} in (_{{ property.python_name }}): diff --git a/openapi_python_client/templates/property_templates/model_property.py.jinja b/openapi_python_client/templates/property_templates/model_property.py.jinja index bdfd3cca5..e7f779563 100644 --- a/openapi_python_client/templates/property_templates/model_property.py.jinja +++ b/openapi_python_client/templates/property_templates/model_property.py.jinja @@ -4,8 +4,8 @@ {% from "property_templates/property_macros.py.jinja" import construct_template %} -{% macro construct(property, source, initial_value=None) %} -{{ construct_template(construct_function, property, source, initial_value=initial_value) }} +{% macro construct(property, source) %} +{{ construct_template(construct_function, property, source) }} {% endmacro %} {% macro check_type_for_construct(property, source) %}isinstance({{ source }}, dict){% endmacro %} diff --git a/openapi_python_client/templates/property_templates/property_macros.py.jinja b/openapi_python_client/templates/property_templates/property_macros.py.jinja index 43adf4c06..52e1d41bc 100644 --- a/openapi_python_client/templates/property_templates/property_macros.py.jinja +++ b/openapi_python_client/templates/property_templates/property_macros.py.jinja @@ -1,4 +1,4 @@ -{% macro construct_template(construct_function, property, source, initial_value=None) %} +{% macro construct_template(construct_function, property, source) %} {% if property.required %} {{ property.python_name }} = {{ construct_function(property, source) }} {% else %}{# Must be non-required #} @@ -6,8 +6,7 @@ _{{ property.python_name }} = {{ source }} {{ property.python_name }}: {{ property.get_type_string() }} {% if not property.required %} if isinstance(_{{ property.python_name }}, Unset): - {{ property.python_name }} = {% if initial_value != None %}{{ initial_value }}{% else %}UNSET{% endif %} - + {{ property.python_name }} = UNSET {% endif %} else: {{ property.python_name }} = {{ construct_function(property, "_" + property.python_name) }} diff --git a/openapi_python_client/templates/property_templates/union_property.py.jinja b/openapi_python_client/templates/property_templates/union_property.py.jinja index df77be05b..b8ab1962d 100644 --- a/openapi_python_client/templates/property_templates/union_property.py.jinja +++ b/openapi_python_client/templates/property_templates/union_property.py.jinja @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value=None) %} +{% macro construct(property, source) %} def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}: {% if "None" in property.get_type_strings_in_union(json=True, multipart=False) %} if data is None: @@ -19,7 +19,7 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri try: if not {{ inner_template.check_type_for_construct(inner_property, "data") }}: raise TypeError() - {{ inner_template.construct(inner_property, "data", initial_value="UNSET") | indent(8) }} + {{ inner_template.construct(inner_property, "data") | indent(8) }} return {{ inner_property.python_name }} except: # noqa: E722 pass @@ -28,7 +28,7 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri if not {{ inner_template.check_type_for_construct(inner_property, "data") }}: raise TypeError() {% endif %} - {{ inner_template.construct(inner_property, "data", initial_value="UNSET") | indent(4) }} + {{ inner_template.construct(inner_property, "data") | indent(4) }} return {{ inner_property.python_name }} {% endif %} {% endfor %} From 1d88d0728f04de8692d9485a696f71571841dff5 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Wed, 3 Jan 2024 17:11:42 -0700 Subject: [PATCH 4/4] Fix required in path check --- openapi_python_client/parser/properties/union.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openapi_python_client/parser/properties/union.py b/openapi_python_client/parser/properties/union.py index 4ec581be1..ec7d18c7e 100644 --- a/openapi_python_client/parser/properties/union.py +++ b/openapi_python_client/parser/properties/union.py @@ -1,7 +1,7 @@ from __future__ import annotations from itertools import chain -from typing import Any, ClassVar +from typing import Any, ClassVar, cast from attr import define, evolve @@ -172,7 +172,9 @@ def get_lazy_imports(self, *, prefix: str) -> set[str]: def validate_location(self, location: oai.ParameterLocation) -> ParseError | None: """Returns an error if this type of property is not allowed in the given location""" + from ..properties import Property + for inner_prop in self.inner_properties: - if inner_prop.validate_location(location) is not None: + if evolve(cast(Property, inner_prop), required=self.required).validate_location(location) is not None: return ParseError(detail=f"{self.get_type_string()} is not allowed in {location}") return None