diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py index 53e2d1def..bf0a91507 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py @@ -7,7 +7,7 @@ Client = httpx.Client import datetime -from typing import Dict, List, Union +from typing import Dict, List, Optional, Union from dateutil.parser import isoparse @@ -42,28 +42,45 @@ def httpx_request( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Response[Union[None, HTTPValidationError]]: - json_datetime_prop: Union[Unset, str] = UNSET - if not isinstance(datetime_prop, Unset): - json_datetime_prop = datetime_prop.isoformat() + json_not_required_not_nullable_datetime_prop: Union[Unset, str] = UNSET + if not isinstance(not_required_not_nullable_datetime_prop, Unset): + json_not_required_not_nullable_datetime_prop = not_required_not_nullable_datetime_prop.isoformat() + + json_not_required_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_nullable_datetime_prop, Unset): + json_not_required_nullable_datetime_prop = ( + not_required_nullable_datetime_prop.isoformat() if not_required_nullable_datetime_prop else None + ) + + json_required_not_nullable_datetime_prop = required_not_nullable_datetime_prop.isoformat() + + json_required_nullable_datetime_prop = ( + required_nullable_datetime_prop.isoformat() if required_nullable_datetime_prop else None + ) json_date_prop: Union[Unset, str] = UNSET if not isinstance(date_prop, Unset): json_date_prop = date_prop.isoformat() - json_list_prop: Union[Unset, List[Any]] = UNSET + json_list_prop: Union[Unset, List[str]] = UNSET if not isinstance(list_prop, Unset): json_list_prop = [] for list_prop_item_data in list_prop: @@ -77,20 +94,20 @@ def httpx_request( else: json_union_prop = union_prop - json_union_prop_with_ref: Union[Unset, float, AnEnum] + json_union_prop_with_ref: Union[Unset, float, str] 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 + json_union_prop_with_ref = union_prop_with_ref.value else: json_union_prop_with_ref = union_prop_with_ref - json_enum_prop: Union[Unset, AnEnum] = UNSET + json_enum_prop: Union[Unset, str] = UNSET if not isinstance(enum_prop, Unset): - json_enum_prop = enum_prop + json_enum_prop = enum_prop.value json_model_prop: Union[Unset, Dict[str, Any]] = UNSET if not isinstance(model_prop, Unset): @@ -98,9 +115,28 @@ def httpx_request( json_required_model_prop = required_model_prop.to_dict() + json_nullable_model_prop: Union[Dict[str, Any], None, Unset] + if isinstance(nullable_model_prop, Unset): + json_nullable_model_prop = UNSET + elif nullable_model_prop is None: + json_nullable_model_prop = None + else: + json_nullable_model_prop = UNSET + if not isinstance(nullable_model_prop, Unset): + json_nullable_model_prop = nullable_model_prop.to_dict() + + json_nullable_required_model_prop: Union[Dict[str, Any], None] + if nullable_required_model_prop is None: + json_nullable_required_model_prop = None + else: + json_nullable_required_model_prop = nullable_required_model_prop.to_dict() + params: Dict[str, Any] = { "string_prop": string_prop, - "datetime_prop": json_datetime_prop, + "not_required_not_nullable_datetime_prop": json_not_required_not_nullable_datetime_prop, + "not_required_nullable_datetime_prop": json_not_required_nullable_datetime_prop, + "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, + "required_nullable_datetime_prop": json_required_nullable_datetime_prop, "date_prop": json_date_prop, "float_prop": float_prop, "int_prop": int_prop, @@ -109,6 +145,8 @@ def httpx_request( "union_prop": json_union_prop, "union_prop_with_ref": json_union_prop_with_ref, "enum_prop": json_enum_prop, + "nullable_model_prop": json_nullable_model_prop, + "nullable_required_model_prop": json_nullable_required_model_prop, } if not isinstance(json_model_prop, Unset): params.update(json_model_prop) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py index 78413711a..244cdc161 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py @@ -39,7 +39,7 @@ def httpx_request( query_param: Union[Unset, List[str]] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: - json_query_param: Union[Unset, List[Any]] = UNSET + json_query_param: Union[Unset, List[str]] = UNSET if not isinstance(query_param, Unset): json_query_param = query_param 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 6f5ac7423..2679739df 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 @@ -17,10 +17,13 @@ ) from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed from .model_with_any_json_properties import ModelWithAnyJsonProperties -from .model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty +from .model_with_any_json_properties_additional_property_type0 import ModelWithAnyJsonPropertiesAdditionalPropertyType0 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 .model_with_union_property_inlined import ModelWithUnionPropertyInlined +from .model_with_union_property_inlined_fruit_type0 import ModelWithUnionPropertyInlinedFruitType0 +from .model_with_union_property_inlined_fruit_type1 import ModelWithUnionPropertyInlinedFruitType1 from .test_inline_objects_json_body import TestInlineObjectsJsonBody from .test_inline_objects_response_200 import TestInlineObjectsResponse_200 from .validation_error import ValidationError 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 9237d2428..91b62c01b 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 @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Optional, Type, TypeVar, Union +from typing import Any, Dict, List, Optional, Type, TypeVar, Union, cast import attr from dateutil.parser import isoparse @@ -10,6 +10,8 @@ from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum +from ..models.free_form_model import FreeFormModel +from ..models.model_with_union_property import ModelWithUnionProperty from ..types import UNSET, Unset T = TypeVar("T", bound="AModel") @@ -20,20 +22,24 @@ class AModel: """ A Model for testing all the ways custom objects can be used """ an_enum_value: AnEnum - a_camel_date_time: Union[datetime.datetime, datetime.date] + a_camel_date_time: Union[datetime.date, datetime.datetime] a_date: datetime.date required_not_nullable: str + one_of_models: Union[FreeFormModel, ModelWithUnionProperty] model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] + nullable_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, None] nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET a_not_required_date: Union[Unset, datetime.date] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET - not_required_nullable: Union[Unset, Optional[str]] = UNSET + not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - not_required_nullable_model: Union[Optional[AModelNotRequiredNullableModel], Unset] = UNSET + not_required_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, Unset] = UNSET + not_required_nullable_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + not_required_nullable_model: Union[Unset, None, AModelNotRequiredNullableModel] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -46,9 +52,15 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable + if isinstance(self.one_of_models, FreeFormModel): + one_of_models = self.one_of_models.to_dict() + + else: + one_of_models = self.one_of_models.to_dict() + model = self.model.to_dict() - nested_list_of_enums: Union[Unset, List[Any]] = UNSET + nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -69,13 +81,53 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable + nullable_one_of_models: Union[Dict[str, Any], None] + if self.nullable_one_of_models is None: + nullable_one_of_models = None + elif isinstance(self.nullable_one_of_models, FreeFormModel): + nullable_one_of_models = self.nullable_one_of_models.to_dict() + + else: + nullable_one_of_models = self.nullable_one_of_models.to_dict() + + not_required_one_of_models: Union[Dict[str, Any], Unset] + 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() + + 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_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 self.not_required_nullable_one_of_models is None: + not_required_nullable_one_of_models = None + 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() + + 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() + + else: + not_required_nullable_one_of_models = self.not_required_nullable_one_of_models + nullable_model = self.nullable_model.to_dict() if self.nullable_model else None not_required_model: Union[Unset, Dict[str, Any]] = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[None, Unset, Dict[str, Any]] = UNSET + not_required_nullable_model: Union[Unset, None, Dict[str, Any]] = UNSET if not isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = ( self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None @@ -88,9 +140,11 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, + "one_of_models": one_of_models, "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, + "nullable_one_of_models": nullable_one_of_models, "nullable_model": nullable_model, } ) @@ -104,6 +158,10 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable + if not_required_one_of_models is not UNSET: + field_dict["not_required_one_of_models"] = not_required_one_of_models + if not_required_nullable_one_of_models is not UNSET: + field_dict["not_required_nullable_one_of_models"] = not_required_nullable_one_of_models if not_required_model is not UNSET: field_dict["not_required_model"] = not_required_model if not_required_nullable_model is not UNSET: @@ -116,18 +174,22 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: 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 - a_camel_date_time: Union[datetime.datetime, datetime.date] + def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.datetime]: try: - a_camel_date_time = isoparse(data) + a_camel_date_time_type0: datetime.datetime + if not isinstance(data, str): + raise TypeError() + a_camel_date_time_type0 = isoparse(data) - return a_camel_date_time + return a_camel_date_time_type0 except: # noqa: E722 pass - a_camel_date_time = isoparse(data).date() + if not isinstance(data, str): + raise TypeError() + a_camel_date_time_type1: datetime.date + a_camel_date_time_type1 = isoparse(data).date() - return a_camel_date_time + return a_camel_date_time_type1 a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) @@ -135,6 +197,25 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") + def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty]: + try: + one_of_models_type0: FreeFormModel + if not isinstance(data, dict): + raise TypeError() + one_of_models_type0 = FreeFormModel.from_dict(data) + + return one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + one_of_models_type1: ModelWithUnionProperty + one_of_models_type1 = ModelWithUnionProperty.from_dict(data) + + return one_of_models_type1 + + one_of_models = _parse_one_of_models(d.pop("one_of_models")) + model = AModelModel.from_dict(d.pop("model")) nested_list_of_enums = [] @@ -167,12 +248,101 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) + def _parse_nullable_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty, None]: + if data is None: + return data + try: + nullable_one_of_models_type0: FreeFormModel + if not isinstance(data, dict): + raise TypeError() + nullable_one_of_models_type0 = FreeFormModel.from_dict(data) + + return nullable_one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + nullable_one_of_models_type1: ModelWithUnionProperty + nullable_one_of_models_type1 = ModelWithUnionProperty.from_dict(data) + + return nullable_one_of_models_type1 + + nullable_one_of_models = _parse_nullable_one_of_models(d.pop("nullable_one_of_models")) + + def _parse_not_required_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty, Unset]: + if isinstance(data, Unset): + return data + try: + not_required_one_of_models_type0: Union[Unset, FreeFormModel] + if not isinstance(data, dict): + raise TypeError() + not_required_one_of_models_type0 = UNSET + _not_required_one_of_models_type0 = data + if not isinstance(_not_required_one_of_models_type0, Unset): + not_required_one_of_models_type0 = FreeFormModel.from_dict(_not_required_one_of_models_type0) + + return not_required_one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + not_required_one_of_models_type1: Union[Unset, ModelWithUnionProperty] + not_required_one_of_models_type1 = UNSET + _not_required_one_of_models_type1 = data + if not isinstance(_not_required_one_of_models_type1, Unset): + not_required_one_of_models_type1 = ModelWithUnionProperty.from_dict(_not_required_one_of_models_type1) + + return not_required_one_of_models_type1 + + not_required_one_of_models = _parse_not_required_one_of_models(d.pop("not_required_one_of_models", UNSET)) + + def _parse_not_required_nullable_one_of_models( + data: object, + ) -> Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + not_required_nullable_one_of_models_type0: Union[Unset, FreeFormModel] + if not isinstance(data, dict): + raise TypeError() + not_required_nullable_one_of_models_type0 = UNSET + _not_required_nullable_one_of_models_type0 = data + if not isinstance(_not_required_nullable_one_of_models_type0, Unset): + not_required_nullable_one_of_models_type0 = FreeFormModel.from_dict( + _not_required_nullable_one_of_models_type0 + ) + + return not_required_nullable_one_of_models_type0 + except: # noqa: E722 + pass + try: + not_required_nullable_one_of_models_type1: Union[Unset, ModelWithUnionProperty] + if not isinstance(data, dict): + raise TypeError() + not_required_nullable_one_of_models_type1 = UNSET + _not_required_nullable_one_of_models_type1 = data + if not isinstance(_not_required_nullable_one_of_models_type1, Unset): + not_required_nullable_one_of_models_type1 = ModelWithUnionProperty.from_dict( + _not_required_nullable_one_of_models_type1 + ) + + return not_required_nullable_one_of_models_type1 + except: # noqa: E722 + pass + return cast(Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str], data) + + not_required_nullable_one_of_models = _parse_not_required_nullable_one_of_models( + d.pop("not_required_nullable_one_of_models", UNSET) + ) + nullable_model = None _nullable_model = d.pop("nullable_model") if _nullable_model is not None: nullable_model = AModelNullableModel.from_dict(_nullable_model) - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET _not_required_model = d.pop("not_required_model", UNSET) if not isinstance(_not_required_model, Unset): not_required_model = AModelNotRequiredModel.from_dict(_not_required_model) @@ -187,6 +357,7 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, + one_of_models=one_of_models, model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, @@ -195,6 +366,9 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, + nullable_one_of_models=nullable_one_of_models, + not_required_one_of_models=not_required_one_of_models, + not_required_nullable_one_of_models=not_required_nullable_one_of_models, nullable_model=nullable_model, not_required_model=not_required_model, not_required_nullable_model=not_required_nullable_model, 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 3025b07af..feb2cdd6b 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 @@ -15,7 +15,7 @@ class HTTPValidationError: detail: Union[Unset, List[ValidationError]] = UNSET def to_dict(self) -> Dict[str, Any]: - detail: Union[Unset, List[Any]] = UNSET + detail: Union[Unset, List[Dict[str, Any]]] = UNSET if not isinstance(self.detail, Unset): detail = [] for detail_item_data in self.detail: diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties.py index 7696b9753..59b3ddb46 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties.py @@ -2,8 +2,9 @@ import attr -from ..models.model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty -from ..types import Unset +from ..models.model_with_any_json_properties_additional_property_type0 import ( + ModelWithAnyJsonPropertiesAdditionalPropertyType0, +) T = TypeVar("T", bound="ModelWithAnyJsonProperties") @@ -13,14 +14,14 @@ class ModelWithAnyJsonProperties: """ """ additional_properties: Dict[ - str, Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool] + str, Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str] ] = 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(): - if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalProperty): + if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalPropertyType0): field_dict[prop_name] = prop.to_dict() elif isinstance(prop, list): @@ -42,25 +43,29 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: for prop_name, prop_dict in d.items(): def _parse_additional_property( - data: Any, - ) -> Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool]: - data = None if isinstance(data, Unset) else data - additional_property: Union[ - ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool - ] + data: object, + ) -> Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str]: try: - additional_property = ModelWithAnyJsonPropertiesAdditionalProperty.from_dict(data) + additional_property_type0: ModelWithAnyJsonPropertiesAdditionalPropertyType0 + if not isinstance(data, dict): + raise TypeError() + additional_property_type0 = ModelWithAnyJsonPropertiesAdditionalPropertyType0.from_dict(data) - return additional_property + return additional_property_type0 except: # noqa: E722 pass try: - additional_property = cast(List[str], data) + additional_property_type1: List[str] + if not isinstance(data, list): + raise TypeError() + additional_property_type1 = cast(List[str], data) - return additional_property + return additional_property_type1 except: # noqa: E722 pass - return cast(Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool], data) + return cast( + Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str], data + ) additional_property = _parse_additional_property(prop_dict) @@ -75,11 +80,13 @@ def additional_keys(self) -> List[str]: def __getitem__( self, key: str - ) -> Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool]: + ) -> Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str]: return self.additional_properties[key] def __setitem__( - self, key: str, value: Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool] + self, + key: str, + value: Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str], ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property_type0.py similarity index 82% rename from end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property.py rename to end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property_type0.py index 69aa84641..76649bf90 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_any_json_properties_additional_property_type0.py @@ -2,11 +2,11 @@ import attr -T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalProperty") +T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalPropertyType0") @attr.s(auto_attribs=True) -class ModelWithAnyJsonPropertiesAdditionalProperty: +class ModelWithAnyJsonPropertiesAdditionalPropertyType0: """ """ additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) @@ -22,10 +22,10 @@ def to_dict(self) -> Dict[str, Any]: @classmethod def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: d = src_dict.copy() - model_with_any_json_properties_additional_property = cls() + model_with_any_json_properties_additional_property_type0 = cls() - model_with_any_json_properties_additional_property.additional_properties = d - return model_with_any_json_properties_additional_property + model_with_any_json_properties_additional_property_type0.additional_properties = d + return model_with_any_json_properties_additional_property_type0 @property def additional_keys(self) -> List[str]: 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 index 19bcdb72b..68e2238dd 100644 --- 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 @@ -14,7 +14,7 @@ class ModelWithPrimitiveAdditionalProperties: """ """ - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) def to_dict(self) -> Dict[str, Any]: @@ -33,7 +33,7 @@ def to_dict(self) -> Dict[str, Any]: @classmethod def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: d = src_dict.copy() - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) if not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict(_a_date_holder) 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 d3984c74e..a3f049533 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 @@ -13,21 +13,21 @@ class ModelWithUnionProperty: """ """ - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + a_property: Union[AnEnum, AnIntEnum, Unset] = UNSET def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] + a_property: Union[Unset, int, str] 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 + a_property = self.a_property.value else: a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value field_dict: Dict[str, Any] = {} field_dict.update({}) @@ -40,24 +40,30 @@ def to_dict(self) -> Dict[str, Any]: def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: 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] + def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: + if isinstance(data, Unset): + return data try: - a_property = UNSET - _a_property = data - if not isinstance(_a_property, Unset): - a_property = AnEnum(_a_property) - - return a_property + a_property_type0: Union[Unset, AnEnum] + if not isinstance(data, str): + raise TypeError() + a_property_type0 = UNSET + _a_property_type0 = data + if not isinstance(_a_property_type0, Unset): + a_property_type0 = AnEnum(_a_property_type0) + + return a_property_type0 except: # noqa: E722 pass - a_property = UNSET - _a_property = data - if not isinstance(_a_property, Unset): - a_property = AnIntEnum(_a_property) - - return a_property + if not isinstance(data, int): + raise TypeError() + a_property_type1: Union[Unset, AnIntEnum] + a_property_type1 = UNSET + _a_property_type1 = data + if not isinstance(_a_property_type1, Unset): + a_property_type1 = AnIntEnum(_a_property_type1) + + return a_property_type1 a_property = _parse_a_property(d.pop("a_property", UNSET)) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined.py new file mode 100644 index 000000000..2349d541d --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined.py @@ -0,0 +1,76 @@ +from typing import Any, Dict, Type, TypeVar, Union + +import attr + +from ..models.model_with_union_property_inlined_fruit_type0 import ModelWithUnionPropertyInlinedFruitType0 +from ..models.model_with_union_property_inlined_fruit_type1 import ModelWithUnionPropertyInlinedFruitType1 +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlined") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlined: + """ """ + + fruit: Union[ModelWithUnionPropertyInlinedFruitType0, ModelWithUnionPropertyInlinedFruitType1, Unset] = UNSET + + def to_dict(self) -> Dict[str, Any]: + fruit: Union[Dict[str, Any], Unset] + 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() + + else: + fruit = UNSET + if not isinstance(self.fruit, Unset): + fruit = self.fruit.to_dict() + + field_dict: Dict[str, Any] = {} + field_dict.update({}) + if fruit is not UNSET: + field_dict["fruit"] = fruit + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + + def _parse_fruit( + data: object, + ) -> Union[ModelWithUnionPropertyInlinedFruitType0, ModelWithUnionPropertyInlinedFruitType1, Unset]: + if isinstance(data, Unset): + return data + try: + fruit_type0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0] + if not isinstance(data, dict): + raise TypeError() + fruit_type0 = UNSET + _fruit_type0 = data + if not isinstance(_fruit_type0, Unset): + fruit_type0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(_fruit_type0) + + return fruit_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + fruit_type1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1] + fruit_type1 = UNSET + _fruit_type1 = data + if not isinstance(_fruit_type1, Unset): + fruit_type1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(_fruit_type1) + + return fruit_type1 + + fruit = _parse_fruit(d.pop("fruit", UNSET)) + + model_with_union_property_inlined = cls( + fruit=fruit, + ) + + return model_with_union_property_inlined diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined_fruit_type0.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined_fruit_type0.py new file mode 100644 index 000000000..da6d11826 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined_fruit_type0.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +import attr + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType0") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlinedFruitType0: + """ """ + + apples: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + apples = self.apples + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if apples is not UNSET: + field_dict["apples"] = apples + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + apples = d.pop("apples", UNSET) + + model_with_union_property_inlined_fruit_type0 = cls( + apples=apples, + ) + + model_with_union_property_inlined_fruit_type0.additional_properties = d + return model_with_union_property_inlined_fruit_type0 + + @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_union_property_inlined_fruit_type1.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined_fruit_type1.py new file mode 100644 index 000000000..32a9b1727 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property_inlined_fruit_type1.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +import attr + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType1") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlinedFruitType1: + """ """ + + bananas: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + bananas = self.bananas + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if bananas is not UNSET: + field_dict["bananas"] = bananas + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + bananas = d.pop("bananas", UNSET) + + model_with_union_property_inlined_fruit_type1 = cls( + bananas=bananas, + ) + + model_with_union_property_inlined_fruit_type1.additional_properties = d + return model_with_union_property_inlined_fruit_type1 + + @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/types.py b/end_to_end_tests/golden-record-custom/custom_e2e/types.py index 2061b9f08..0739e2197 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/types.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/types.py @@ -11,6 +11,8 @@ def __bool__(self) -> bool: UNSET: Unset = Unset() +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +22,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py index 13080dc97..b6aaaea6e 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py @@ -15,32 +15,49 @@ def _get_kwargs( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Dict[str, Any]: url = "{}/tests/defaults".format(client.base_url) headers: Dict[str, Any] = client.get_headers() cookies: Dict[str, Any] = client.get_cookies() - json_datetime_prop: Union[Unset, str] = UNSET - if not isinstance(datetime_prop, Unset): - json_datetime_prop = datetime_prop.isoformat() + json_not_required_not_nullable_datetime_prop: Union[Unset, str] = UNSET + if not isinstance(not_required_not_nullable_datetime_prop, Unset): + json_not_required_not_nullable_datetime_prop = not_required_not_nullable_datetime_prop.isoformat() + + json_not_required_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_nullable_datetime_prop, Unset): + json_not_required_nullable_datetime_prop = ( + not_required_nullable_datetime_prop.isoformat() if not_required_nullable_datetime_prop else None + ) + + json_required_not_nullable_datetime_prop = required_not_nullable_datetime_prop.isoformat() + + json_required_nullable_datetime_prop = ( + required_nullable_datetime_prop.isoformat() if required_nullable_datetime_prop else None + ) json_date_prop: Union[Unset, str] = UNSET if not isinstance(date_prop, Unset): json_date_prop = date_prop.isoformat() - json_list_prop: Union[Unset, List[Any]] = UNSET + json_list_prop: Union[Unset, List[str]] = UNSET if not isinstance(list_prop, Unset): json_list_prop = [] for list_prop_item_data in list_prop: @@ -54,20 +71,20 @@ def _get_kwargs( else: json_union_prop = union_prop - json_union_prop_with_ref: Union[Unset, float, AnEnum] + json_union_prop_with_ref: Union[Unset, float, str] 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 + json_union_prop_with_ref = union_prop_with_ref.value else: json_union_prop_with_ref = union_prop_with_ref - json_enum_prop: Union[Unset, AnEnum] = UNSET + json_enum_prop: Union[Unset, str] = UNSET if not isinstance(enum_prop, Unset): - json_enum_prop = enum_prop + json_enum_prop = enum_prop.value json_model_prop: Union[Unset, Dict[str, Any]] = UNSET if not isinstance(model_prop, Unset): @@ -75,9 +92,28 @@ def _get_kwargs( json_required_model_prop = required_model_prop.to_dict() + json_nullable_model_prop: Union[Dict[str, Any], None, Unset] + if isinstance(nullable_model_prop, Unset): + json_nullable_model_prop = UNSET + elif nullable_model_prop is None: + json_nullable_model_prop = None + else: + json_nullable_model_prop = UNSET + if not isinstance(nullable_model_prop, Unset): + json_nullable_model_prop = nullable_model_prop.to_dict() + + json_nullable_required_model_prop: Union[Dict[str, Any], None] + if nullable_required_model_prop is None: + json_nullable_required_model_prop = None + else: + json_nullable_required_model_prop = nullable_required_model_prop.to_dict() + params: Dict[str, Any] = { "string_prop": string_prop, - "datetime_prop": json_datetime_prop, + "not_required_not_nullable_datetime_prop": json_not_required_not_nullable_datetime_prop, + "not_required_nullable_datetime_prop": json_not_required_nullable_datetime_prop, + "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, + "required_nullable_datetime_prop": json_required_nullable_datetime_prop, "date_prop": json_date_prop, "float_prop": float_prop, "int_prop": int_prop, @@ -86,6 +122,8 @@ def _get_kwargs( "union_prop": json_union_prop, "union_prop_with_ref": json_union_prop_with_ref, "enum_prop": json_enum_prop, + "nullable_model_prop": json_nullable_model_prop, + "nullable_required_model_prop": json_nullable_required_model_prop, } if not isinstance(json_model_prop, Unset): params.update(json_model_prop) @@ -126,22 +164,30 @@ def sync_detailed( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -152,6 +198,8 @@ def sync_detailed( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, + nullable_model_prop=nullable_model_prop, + nullable_required_model_prop=nullable_required_model_prop, ) response = httpx.post( @@ -165,24 +213,32 @@ def sync( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Optional[Union[None, HTTPValidationError]]: """ """ return sync_detailed( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -193,6 +249,8 @@ def sync( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, + nullable_model_prop=nullable_model_prop, + nullable_required_model_prop=nullable_required_model_prop, ).parsed @@ -200,22 +258,30 @@ async def asyncio_detailed( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -226,6 +292,8 @@ async def asyncio_detailed( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, + nullable_model_prop=nullable_model_prop, + nullable_required_model_prop=nullable_required_model_prop, ) async with httpx.AsyncClient() as _client: @@ -238,17 +306,22 @@ async def asyncio( *, client: Client, string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_not_nullable_datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), float_prop: Union[Unset, float] = 3.14, int_prop: Union[Unset, int] = 7, boolean_prop: Union[Unset, bool] = False, list_prop: Union[Unset, List[AnEnum]] = UNSET, union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, + union_prop_with_ref: Union[AnEnum, Unset, float] = 0.6, enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + model_prop: Union[Unset, ModelWithUnionProperty] = UNSET, required_model_prop: ModelWithUnionProperty, + nullable_model_prop: Union[ModelWithUnionProperty, None, Unset] = UNSET, + nullable_required_model_prop: Union[ModelWithUnionProperty, None], ) -> Optional[Union[None, HTTPValidationError]]: """ """ @@ -256,7 +329,10 @@ async def asyncio( await asyncio_detailed( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -267,5 +343,7 @@ async def asyncio( enum_prop=enum_prop, model_prop=model_prop, required_model_prop=required_model_prop, + nullable_model_prop=nullable_model_prop, + nullable_required_model_prop=nullable_required_model_prop, ) ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py index f974c9c07..64431ba2f 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py @@ -17,7 +17,7 @@ def _get_kwargs( headers: Dict[str, Any] = client.get_headers() cookies: Dict[str, Any] = client.get_cookies() - json_query_param: Union[Unset, List[Any]] = UNSET + json_query_param: Union[Unset, List[str]] = UNSET if not isinstance(query_param, Unset): json_query_param = query_param 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 6f5ac7423..2679739df 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 @@ -17,10 +17,13 @@ ) from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed from .model_with_any_json_properties import ModelWithAnyJsonProperties -from .model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty +from .model_with_any_json_properties_additional_property_type0 import ModelWithAnyJsonPropertiesAdditionalPropertyType0 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 .model_with_union_property_inlined import ModelWithUnionPropertyInlined +from .model_with_union_property_inlined_fruit_type0 import ModelWithUnionPropertyInlinedFruitType0 +from .model_with_union_property_inlined_fruit_type1 import ModelWithUnionPropertyInlinedFruitType1 from .test_inline_objects_json_body import TestInlineObjectsJsonBody from .test_inline_objects_response_200 import TestInlineObjectsResponse_200 from .validation_error import ValidationError 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 9237d2428..91b62c01b 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 @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Optional, Type, TypeVar, Union +from typing import Any, Dict, List, Optional, Type, TypeVar, Union, cast import attr from dateutil.parser import isoparse @@ -10,6 +10,8 @@ from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum +from ..models.free_form_model import FreeFormModel +from ..models.model_with_union_property import ModelWithUnionProperty from ..types import UNSET, Unset T = TypeVar("T", bound="AModel") @@ -20,20 +22,24 @@ class AModel: """ A Model for testing all the ways custom objects can be used """ an_enum_value: AnEnum - a_camel_date_time: Union[datetime.datetime, datetime.date] + a_camel_date_time: Union[datetime.date, datetime.datetime] a_date: datetime.date required_not_nullable: str + one_of_models: Union[FreeFormModel, ModelWithUnionProperty] model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] + nullable_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, None] nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET a_not_required_date: Union[Unset, datetime.date] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET - not_required_nullable: Union[Unset, Optional[str]] = UNSET + not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - not_required_nullable_model: Union[Optional[AModelNotRequiredNullableModel], Unset] = UNSET + not_required_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, Unset] = UNSET + not_required_nullable_one_of_models: Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + not_required_nullable_model: Union[Unset, None, AModelNotRequiredNullableModel] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -46,9 +52,15 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable + if isinstance(self.one_of_models, FreeFormModel): + one_of_models = self.one_of_models.to_dict() + + else: + one_of_models = self.one_of_models.to_dict() + model = self.model.to_dict() - nested_list_of_enums: Union[Unset, List[Any]] = UNSET + nested_list_of_enums: Union[Unset, List[List[str]]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] for nested_list_of_enums_item_data in self.nested_list_of_enums: @@ -69,13 +81,53 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable + nullable_one_of_models: Union[Dict[str, Any], None] + if self.nullable_one_of_models is None: + nullable_one_of_models = None + elif isinstance(self.nullable_one_of_models, FreeFormModel): + nullable_one_of_models = self.nullable_one_of_models.to_dict() + + else: + nullable_one_of_models = self.nullable_one_of_models.to_dict() + + not_required_one_of_models: Union[Dict[str, Any], Unset] + 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() + + 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_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 self.not_required_nullable_one_of_models is None: + not_required_nullable_one_of_models = None + 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() + + 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() + + else: + not_required_nullable_one_of_models = self.not_required_nullable_one_of_models + nullable_model = self.nullable_model.to_dict() if self.nullable_model else None not_required_model: Union[Unset, Dict[str, Any]] = UNSET if not isinstance(self.not_required_model, Unset): not_required_model = self.not_required_model.to_dict() - not_required_nullable_model: Union[None, Unset, Dict[str, Any]] = UNSET + not_required_nullable_model: Union[Unset, None, Dict[str, Any]] = UNSET if not isinstance(self.not_required_nullable_model, Unset): not_required_nullable_model = ( self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None @@ -88,9 +140,11 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, + "one_of_models": one_of_models, "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, + "nullable_one_of_models": nullable_one_of_models, "nullable_model": nullable_model, } ) @@ -104,6 +158,10 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable + if not_required_one_of_models is not UNSET: + field_dict["not_required_one_of_models"] = not_required_one_of_models + if not_required_nullable_one_of_models is not UNSET: + field_dict["not_required_nullable_one_of_models"] = not_required_nullable_one_of_models if not_required_model is not UNSET: field_dict["not_required_model"] = not_required_model if not_required_nullable_model is not UNSET: @@ -116,18 +174,22 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: 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 - a_camel_date_time: Union[datetime.datetime, datetime.date] + def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.datetime]: try: - a_camel_date_time = isoparse(data) + a_camel_date_time_type0: datetime.datetime + if not isinstance(data, str): + raise TypeError() + a_camel_date_time_type0 = isoparse(data) - return a_camel_date_time + return a_camel_date_time_type0 except: # noqa: E722 pass - a_camel_date_time = isoparse(data).date() + if not isinstance(data, str): + raise TypeError() + a_camel_date_time_type1: datetime.date + a_camel_date_time_type1 = isoparse(data).date() - return a_camel_date_time + return a_camel_date_time_type1 a_camel_date_time = _parse_a_camel_date_time(d.pop("aCamelDateTime")) @@ -135,6 +197,25 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") + def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty]: + try: + one_of_models_type0: FreeFormModel + if not isinstance(data, dict): + raise TypeError() + one_of_models_type0 = FreeFormModel.from_dict(data) + + return one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + one_of_models_type1: ModelWithUnionProperty + one_of_models_type1 = ModelWithUnionProperty.from_dict(data) + + return one_of_models_type1 + + one_of_models = _parse_one_of_models(d.pop("one_of_models")) + model = AModelModel.from_dict(d.pop("model")) nested_list_of_enums = [] @@ -167,12 +248,101 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) + def _parse_nullable_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty, None]: + if data is None: + return data + try: + nullable_one_of_models_type0: FreeFormModel + if not isinstance(data, dict): + raise TypeError() + nullable_one_of_models_type0 = FreeFormModel.from_dict(data) + + return nullable_one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + nullable_one_of_models_type1: ModelWithUnionProperty + nullable_one_of_models_type1 = ModelWithUnionProperty.from_dict(data) + + return nullable_one_of_models_type1 + + nullable_one_of_models = _parse_nullable_one_of_models(d.pop("nullable_one_of_models")) + + def _parse_not_required_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty, Unset]: + if isinstance(data, Unset): + return data + try: + not_required_one_of_models_type0: Union[Unset, FreeFormModel] + if not isinstance(data, dict): + raise TypeError() + not_required_one_of_models_type0 = UNSET + _not_required_one_of_models_type0 = data + if not isinstance(_not_required_one_of_models_type0, Unset): + not_required_one_of_models_type0 = FreeFormModel.from_dict(_not_required_one_of_models_type0) + + return not_required_one_of_models_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + not_required_one_of_models_type1: Union[Unset, ModelWithUnionProperty] + not_required_one_of_models_type1 = UNSET + _not_required_one_of_models_type1 = data + if not isinstance(_not_required_one_of_models_type1, Unset): + not_required_one_of_models_type1 = ModelWithUnionProperty.from_dict(_not_required_one_of_models_type1) + + return not_required_one_of_models_type1 + + not_required_one_of_models = _parse_not_required_one_of_models(d.pop("not_required_one_of_models", UNSET)) + + def _parse_not_required_nullable_one_of_models( + data: object, + ) -> Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + not_required_nullable_one_of_models_type0: Union[Unset, FreeFormModel] + if not isinstance(data, dict): + raise TypeError() + not_required_nullable_one_of_models_type0 = UNSET + _not_required_nullable_one_of_models_type0 = data + if not isinstance(_not_required_nullable_one_of_models_type0, Unset): + not_required_nullable_one_of_models_type0 = FreeFormModel.from_dict( + _not_required_nullable_one_of_models_type0 + ) + + return not_required_nullable_one_of_models_type0 + except: # noqa: E722 + pass + try: + not_required_nullable_one_of_models_type1: Union[Unset, ModelWithUnionProperty] + if not isinstance(data, dict): + raise TypeError() + not_required_nullable_one_of_models_type1 = UNSET + _not_required_nullable_one_of_models_type1 = data + if not isinstance(_not_required_nullable_one_of_models_type1, Unset): + not_required_nullable_one_of_models_type1 = ModelWithUnionProperty.from_dict( + _not_required_nullable_one_of_models_type1 + ) + + return not_required_nullable_one_of_models_type1 + except: # noqa: E722 + pass + return cast(Union[FreeFormModel, ModelWithUnionProperty, None, Unset, str], data) + + not_required_nullable_one_of_models = _parse_not_required_nullable_one_of_models( + d.pop("not_required_nullable_one_of_models", UNSET) + ) + nullable_model = None _nullable_model = d.pop("nullable_model") if _nullable_model is not None: nullable_model = AModelNullableModel.from_dict(_nullable_model) - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET _not_required_model = d.pop("not_required_model", UNSET) if not isinstance(_not_required_model, Unset): not_required_model = AModelNotRequiredModel.from_dict(_not_required_model) @@ -187,6 +357,7 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, + one_of_models=one_of_models, model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, @@ -195,6 +366,9 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, + nullable_one_of_models=nullable_one_of_models, + not_required_one_of_models=not_required_one_of_models, + not_required_nullable_one_of_models=not_required_nullable_one_of_models, nullable_model=nullable_model, not_required_model=not_required_model, not_required_nullable_model=not_required_nullable_model, 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 3025b07af..feb2cdd6b 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 @@ -15,7 +15,7 @@ class HTTPValidationError: detail: Union[Unset, List[ValidationError]] = UNSET def to_dict(self) -> Dict[str, Any]: - detail: Union[Unset, List[Any]] = UNSET + detail: Union[Unset, List[Dict[str, Any]]] = UNSET if not isinstance(self.detail, Unset): detail = [] for detail_item_data in self.detail: diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py index 7696b9753..59b3ddb46 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py @@ -2,8 +2,9 @@ import attr -from ..models.model_with_any_json_properties_additional_property import ModelWithAnyJsonPropertiesAdditionalProperty -from ..types import Unset +from ..models.model_with_any_json_properties_additional_property_type0 import ( + ModelWithAnyJsonPropertiesAdditionalPropertyType0, +) T = TypeVar("T", bound="ModelWithAnyJsonProperties") @@ -13,14 +14,14 @@ class ModelWithAnyJsonProperties: """ """ additional_properties: Dict[ - str, Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool] + str, Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str] ] = 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(): - if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalProperty): + if isinstance(prop, ModelWithAnyJsonPropertiesAdditionalPropertyType0): field_dict[prop_name] = prop.to_dict() elif isinstance(prop, list): @@ -42,25 +43,29 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: for prop_name, prop_dict in d.items(): def _parse_additional_property( - data: Any, - ) -> Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool]: - data = None if isinstance(data, Unset) else data - additional_property: Union[ - ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool - ] + data: object, + ) -> Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str]: try: - additional_property = ModelWithAnyJsonPropertiesAdditionalProperty.from_dict(data) + additional_property_type0: ModelWithAnyJsonPropertiesAdditionalPropertyType0 + if not isinstance(data, dict): + raise TypeError() + additional_property_type0 = ModelWithAnyJsonPropertiesAdditionalPropertyType0.from_dict(data) - return additional_property + return additional_property_type0 except: # noqa: E722 pass try: - additional_property = cast(List[str], data) + additional_property_type1: List[str] + if not isinstance(data, list): + raise TypeError() + additional_property_type1 = cast(List[str], data) - return additional_property + return additional_property_type1 except: # noqa: E722 pass - return cast(Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool], data) + return cast( + Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str], data + ) additional_property = _parse_additional_property(prop_dict) @@ -75,11 +80,13 @@ def additional_keys(self) -> List[str]: def __getitem__( self, key: str - ) -> Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool]: + ) -> Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str]: return self.additional_properties[key] def __setitem__( - self, key: str, value: Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool] + self, + key: str, + value: Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str], ) -> None: self.additional_properties[key] = value diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type0.py similarity index 82% rename from end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py rename to end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type0.py index 69aa84641..76649bf90 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties_additional_property_type0.py @@ -2,11 +2,11 @@ import attr -T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalProperty") +T = TypeVar("T", bound="ModelWithAnyJsonPropertiesAdditionalPropertyType0") @attr.s(auto_attribs=True) -class ModelWithAnyJsonPropertiesAdditionalProperty: +class ModelWithAnyJsonPropertiesAdditionalPropertyType0: """ """ additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) @@ -22,10 +22,10 @@ def to_dict(self) -> Dict[str, Any]: @classmethod def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: d = src_dict.copy() - model_with_any_json_properties_additional_property = cls() + model_with_any_json_properties_additional_property_type0 = cls() - model_with_any_json_properties_additional_property.additional_properties = d - return model_with_any_json_properties_additional_property + model_with_any_json_properties_additional_property_type0.additional_properties = d + return model_with_any_json_properties_additional_property_type0 @property def additional_keys(self) -> List[str]: 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 index 19bcdb72b..68e2238dd 100644 --- 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 @@ -14,7 +14,7 @@ class ModelWithPrimitiveAdditionalProperties: """ """ - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) def to_dict(self) -> Dict[str, Any]: @@ -33,7 +33,7 @@ def to_dict(self) -> Dict[str, Any]: @classmethod def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: d = src_dict.copy() - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) if not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict(_a_date_holder) 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 d3984c74e..a3f049533 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 @@ -13,21 +13,21 @@ class ModelWithUnionProperty: """ """ - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + a_property: Union[AnEnum, AnIntEnum, Unset] = UNSET def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] + a_property: Union[Unset, int, str] 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 + a_property = self.a_property.value else: a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value field_dict: Dict[str, Any] = {} field_dict.update({}) @@ -40,24 +40,30 @@ def to_dict(self) -> Dict[str, Any]: def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: 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] + def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]: + if isinstance(data, Unset): + return data try: - a_property = UNSET - _a_property = data - if not isinstance(_a_property, Unset): - a_property = AnEnum(_a_property) - - return a_property + a_property_type0: Union[Unset, AnEnum] + if not isinstance(data, str): + raise TypeError() + a_property_type0 = UNSET + _a_property_type0 = data + if not isinstance(_a_property_type0, Unset): + a_property_type0 = AnEnum(_a_property_type0) + + return a_property_type0 except: # noqa: E722 pass - a_property = UNSET - _a_property = data - if not isinstance(_a_property, Unset): - a_property = AnIntEnum(_a_property) - - return a_property + if not isinstance(data, int): + raise TypeError() + a_property_type1: Union[Unset, AnIntEnum] + a_property_type1 = UNSET + _a_property_type1 = data + if not isinstance(_a_property_type1, Unset): + a_property_type1 = AnIntEnum(_a_property_type1) + + return a_property_type1 a_property = _parse_a_property(d.pop("a_property", UNSET)) 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 new file mode 100644 index 000000000..2349d541d --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py @@ -0,0 +1,76 @@ +from typing import Any, Dict, Type, TypeVar, Union + +import attr + +from ..models.model_with_union_property_inlined_fruit_type0 import ModelWithUnionPropertyInlinedFruitType0 +from ..models.model_with_union_property_inlined_fruit_type1 import ModelWithUnionPropertyInlinedFruitType1 +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlined") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlined: + """ """ + + fruit: Union[ModelWithUnionPropertyInlinedFruitType0, ModelWithUnionPropertyInlinedFruitType1, Unset] = UNSET + + def to_dict(self) -> Dict[str, Any]: + fruit: Union[Dict[str, Any], Unset] + 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() + + else: + fruit = UNSET + if not isinstance(self.fruit, Unset): + fruit = self.fruit.to_dict() + + field_dict: Dict[str, Any] = {} + field_dict.update({}) + if fruit is not UNSET: + field_dict["fruit"] = fruit + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + + def _parse_fruit( + data: object, + ) -> Union[ModelWithUnionPropertyInlinedFruitType0, ModelWithUnionPropertyInlinedFruitType1, Unset]: + if isinstance(data, Unset): + return data + try: + fruit_type0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0] + if not isinstance(data, dict): + raise TypeError() + fruit_type0 = UNSET + _fruit_type0 = data + if not isinstance(_fruit_type0, Unset): + fruit_type0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(_fruit_type0) + + return fruit_type0 + except: # noqa: E722 + pass + if not isinstance(data, dict): + raise TypeError() + fruit_type1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1] + fruit_type1 = UNSET + _fruit_type1 = data + if not isinstance(_fruit_type1, Unset): + fruit_type1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(_fruit_type1) + + return fruit_type1 + + fruit = _parse_fruit(d.pop("fruit", UNSET)) + + model_with_union_property_inlined = cls( + fruit=fruit, + ) + + return model_with_union_property_inlined diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type0.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type0.py new file mode 100644 index 000000000..da6d11826 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type0.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +import attr + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType0") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlinedFruitType0: + """ """ + + apples: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + apples = self.apples + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if apples is not UNSET: + field_dict["apples"] = apples + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + apples = d.pop("apples", UNSET) + + model_with_union_property_inlined_fruit_type0 = cls( + apples=apples, + ) + + model_with_union_property_inlined_fruit_type0.additional_properties = d + return model_with_union_property_inlined_fruit_type0 + + @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_union_property_inlined_fruit_type1.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type1.py new file mode 100644 index 000000000..32a9b1727 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined_fruit_type1.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Type, TypeVar, Union + +import attr + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="ModelWithUnionPropertyInlinedFruitType1") + + +@attr.s(auto_attribs=True) +class ModelWithUnionPropertyInlinedFruitType1: + """ """ + + bananas: Union[Unset, str] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + bananas = self.bananas + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if bananas is not UNSET: + field_dict["bananas"] = bananas + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + d = src_dict.copy() + bananas = d.pop("bananas", UNSET) + + model_with_union_property_inlined_fruit_type1 = cls( + bananas=bananas, + ) + + model_with_union_property_inlined_fruit_type1.additional_properties = d + return model_with_union_property_inlined_fruit_type1 + + @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/types.py b/end_to_end_tests/golden-record/my_test_api_client/types.py index 2061b9f08..0739e2197 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/types.py @@ -11,6 +11,8 @@ def __bool__(self) -> bool: UNSET: Unset = Unset() +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +22,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index fcd83e460..3cc49b58d 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -290,12 +290,49 @@ { "required": false, "schema": { - "title": "Datetime Prop", + "title": "Not Required, Not Nullable Datetime Prop", + "nullable": false, "type": "string", "format": "date-time", "default": "1010-10-10T00:00:00" }, - "name": "datetime_prop", + "name": "not_required_not_nullable_datetime_prop", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Not Required, Nullable Datetime Prop", + "nullable": true, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "not_required_nullable_datetime_prop", + "in": "query" + }, + { + "required": true, + "schema": { + "title": "Required, Not Nullable Datetime Prop", + "nullable": false, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "required_not_nullable_datetime_prop", + "in": "query" + }, + { + "required": true, + "schema": { + "title": "Required, Nullable Datetime Prop", + "nullable": true, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "required_nullable_datetime_prop", "in": "query" }, { @@ -412,6 +449,32 @@ }, "name": "required_model_prop", "in": "query" + }, + { + "required": false, + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": true + }, + "name": "nullable_model_prop", + "in": "query" + }, + { + "required": true, + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": true + }, + "name": "nullable_required_model_prop", + "in": "query" } ], "responses": { @@ -672,7 +735,18 @@ "schemas": { "AModel": { "title": "AModel", - "required": ["an_enum_value", "aCamelDateTime", "a_date", "a_nullable_date", "required_nullable", "required_not_nullable", "model", "nullable_model"], + "required": [ + "an_enum_value", + "aCamelDateTime", + "a_date", + "a_nullable_date", + "required_nullable", + "required_not_nullable", + "model", + "nullable_model", + "one_of_models", + "nullable_one_of_models" + ], "type": "object", "properties": { "an_enum_value": { @@ -742,6 +816,53 @@ "type": "string", "nullable": false }, + "one_of_models": { + "oneOf": [ + { + "ref": "#components/schemas/FreeFormModel" + }, + { + "ref": "#components/schemas/ModelWithUnionProperty" + } + ], + "nullable": false + }, + "nullable_one_of_models": { + "oneOf": [ + { + "ref": "#components/schemas/FreeFormModel" + }, + { + "ref": "#components/schemas/ModelWithUnionProperty" + } + ], + "nullable": true + }, + "not_required_one_of_models": { + "oneOf": [ + { + "ref": "#components/schemas/FreeFormModel" + }, + { + "ref": "#components/schemas/ModelWithUnionProperty" + } + ], + "nullable": false + }, + "not_required_nullable_one_of_models": { + "oneOf": [ + { + "ref": "#components/schemas/FreeFormModel" + }, + { + "ref": "#components/schemas/ModelWithUnionProperty" + }, + { + "type": "string" + } + ], + "nullable": true + }, "model": { "type": "object", "allOf": [ @@ -861,6 +982,29 @@ }, "additionalProperties": false }, + "ModelWithUnionPropertyInlined": { + "title": "ModelWithUnionPropertyInlined", + "type": "object", + "properties": { + "fruit": { + "oneOf": [ + { + "type": "object", + "properties": { + "apples": {"type": "string"} + } + }, + { + "type": "object", + "properties": { + "bananas": {"type": "string"} + } + } + ] + } + }, + "additionalProperties": false + }, "FreeFormModel": { "title": "FreeFormModel", "type": "object" diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 992d726df..f3cfa7ff9 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -19,6 +19,7 @@ class NoneProperty(Property): """ A property that is always None (used for empty schemas) """ _type_string: ClassVar[str] = "None" + _json_type_string: ClassVar[str] = "None" template: ClassVar[Optional[str]] = "none_property.py.jinja" @@ -29,6 +30,7 @@ class StringProperty(Property): max_length: Optional[int] = None pattern: Optional[str] = None _type_string: ClassVar[str] = "str" + _json_type_string: ClassVar[str] = "str" @attr.s(auto_attribs=True, frozen=True) @@ -38,6 +40,7 @@ class DateTimeProperty(Property): """ _type_string: ClassVar[str] = "datetime.datetime" + _json_type_string: ClassVar[str] = "str" template: ClassVar[str] = "datetime_property.py.jinja" def get_imports(self, *, prefix: str) -> Set[str]: @@ -58,6 +61,7 @@ class DateProperty(Property): """ A property of type datetime.date """ _type_string: ClassVar[str] = "datetime.date" + _json_type_string: ClassVar[str] = "str" template: ClassVar[str] = "date_property.py.jinja" def get_imports(self, *, prefix: str) -> Set[str]: @@ -78,6 +82,8 @@ class FileProperty(Property): """ A property used for uploading files """ _type_string: ClassVar[str] = "File" + # Return type of File.to_tuple() + _json_type_string: ClassVar[str] = "Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]" template: ClassVar[str] = "file_property.py.jinja" def get_imports(self, *, prefix: str) -> Set[str]: @@ -98,6 +104,7 @@ class FloatProperty(Property): """ A property of type float """ _type_string: ClassVar[str] = "float" + _json_type_string: ClassVar[str] = "float" @attr.s(auto_attribs=True, frozen=True) @@ -105,6 +112,7 @@ class IntProperty(Property): """ A property of type int """ _type_string: ClassVar[str] = "int" + _json_type_string: ClassVar[str] = "int" @attr.s(auto_attribs=True, frozen=True) @@ -112,6 +120,7 @@ class BooleanProperty(Property): """ Property for bool """ _type_string: ClassVar[str] = "bool" + _json_type_string: ClassVar[str] = "bool" InnerProp = TypeVar("InnerProp", bound=Property) @@ -124,16 +133,11 @@ class ListProperty(Property, Generic[InnerProp]): inner_property: InnerProp template: ClassVar[str] = "list_property.py.jinja" - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - type_string = f"List[{self.inner_property.get_type_string()}]" - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + def get_base_type_string(self) -> str: + return f"List[{self.inner_property.get_type_string()}]" + + def get_base_json_type_string(self) -> str: + return f"List[{self.inner_property.get_type_string(json=True)}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -167,18 +171,39 @@ def __attrs_post_init__(self) -> None: self, "has_properties_without_templates", any(prop.template is None for prop in self.inner_properties) ) - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - inner_types = [p.get_type_string(no_optional=True) for p in self.inner_properties] - inner_prop_string = ", ".join(inner_types) - type_string = f"Union[{inner_prop_string}]" + def _get_inner_type_strings(self, json: bool = False) -> Set[str]: + return {p.get_type_string(no_optional=True, json=json) for p in self.inner_properties} + + def _get_type_string_from_inner_type_strings(self, inner_types: Set[str]) -> str: + if len(inner_types) == 1: + return inner_types.pop() + else: + return f"Union[{', '.join(sorted(inner_types))}]" + + def get_base_type_string(self) -> str: + return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=False)) + + def get_base_json_type_string(self) -> str: + return self._get_type_string_from_inner_type_strings(self._get_inner_type_strings(json=True)) + + def get_type_strings_in_union(self, no_optional: bool = False, json: bool = False) -> Set[str]: + type_strings = self._get_inner_type_strings(json=json) if no_optional: - return type_string - if not self.required: - type_string = f"Union[Unset, {inner_prop_string}]" + return type_strings if self.nullable: - type_string = f"Optional[{type_string}]" - return type_string + type_strings.add("None") + if not self.required: + type_strings.add("Unset") + return type_strings + + def get_type_string(self, no_optional: bool = False, json: bool = False) -> str: + """ + Get a string representation of type that should be used when declaring this property. + This implementation differs slightly from `Property.get_type_string` in order to collapse + nested union types. + """ + type_strings_in_union = self.get_type_strings_in_union(no_optional=no_optional, json=json) + return self._get_type_string_from_inner_type_strings(type_strings_in_union) def get_imports(self, *, prefix: str) -> Set[str]: """ @@ -388,9 +413,9 @@ def build_union_property( *, data: oai.Schema, name: str, required: bool, schemas: Schemas, parent_name: str ) -> Tuple[Union[UnionProperty, PropertyError], Schemas]: sub_properties: List[Property] = [] - for sub_prop_data in chain(data.anyOf, data.oneOf): + for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf)): sub_prop, schemas = property_from_data( - name=name, required=required, data=sub_prop_data, schemas=schemas, parent_name=parent_name + name=f"{name}_type{i}", required=required, data=sub_prop_data, schemas=schemas, parent_name=parent_name ) if isinstance(sub_prop, PropertyError): return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), schemas diff --git a/openapi_python_client/parser/properties/enum_property.py b/openapi_python_client/parser/properties/enum_property.py index 7549ba1a8..0e095e455 100644 --- a/openapi_python_client/parser/properties/enum_property.py +++ b/openapi_python_client/parser/properties/enum_property.py @@ -22,17 +22,11 @@ class EnumProperty(Property): template: ClassVar[str] = "enum_property.py.jinja" - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ + def get_base_type_string(self, json: bool = False) -> str: + return self.reference.class_name - type_string = self.reference.class_name - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + def get_base_json_type_string(self, json: bool = False) -> str: + return self.value_type.__name__ def get_imports(self, *, prefix: str) -> Set[str]: """ diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 1cf3e455c..0439a4ca4 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -17,20 +17,13 @@ class ModelProperty(Property): description: str relative_imports: Set[str] additional_properties: Union[bool, Property] + _json_type_string: ClassVar[str] = "Dict[str, Any]" template: ClassVar[str] = "model_property.py.jinja" json_is_dict: ClassVar[bool] = True - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - type_string = self.reference.class_name - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[{type_string}, Unset]" - return type_string + def get_base_type_string(self, json: bool = False) -> str: + return self.reference.class_name def get_imports(self, *, prefix: str) -> Set[str]: """ diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index a94af72ba..7eb5161f9 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -24,6 +24,7 @@ class Property: required: bool nullable: bool _type_string: ClassVar[str] = "" + _json_type_string: ClassVar[str] = "" # Type of the property after JSON serialization default: Optional[str] = attr.ib() python_name: str = attr.ib(init=False) @@ -33,21 +34,33 @@ class Property: def __attrs_post_init__(self) -> None: object.__setattr__(self, "python_name", utils.to_valid_python_identifier(utils.snake_case(self.name))) - def get_type_string(self, no_optional: bool = False) -> str: + def get_base_type_string(self) -> str: + return self._type_string + + def get_base_json_type_string(self) -> str: + return self._json_type_string + + def get_type_string(self, no_optional: bool = False, json: 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 """ - type_string = self._type_string - if no_optional: - return self._type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + if json: + type_string = self.get_base_json_type_string() + else: + type_string = self.get_base_type_string() + + if no_optional or (self.required and not self.nullable): + return type_string + elif self.required and self.nullable: + return f"Optional[{type_string}]" + elif not self.required and self.nullable: + return f"Union[Unset, None, {type_string}]" + else: + return f"Union[Unset, {type_string}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" 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 0ecfa84cb..65672d2e7 100644 --- a/openapi_python_client/templates/property_templates/date_property.py.jinja +++ b/openapi_python_client/templates/property_templates/date_property.py.jinja @@ -8,11 +8,13 @@ isoparse({{ source }}).date() {{ construct_template(construct_function, property, source, initial_value=initial_value) }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {% if property.required %} {{ destination }} = {{ source }}.isoformat() {% if property.nullable %}if {{ source }} else None {%endif%} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, str]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} {{ destination }} = {{ source }}.isoformat() if {{ source }} else None 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 692e12fde..de1e8427f 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.py.jinja +++ b/openapi_python_client/templates/property_templates/datetime_property.py.jinja @@ -8,6 +8,8 @@ isoparse({{ source }}) {{ construct_template(construct_function, property, source, initial_value=initial_value) }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {% if property.required %} {% if property.nullable %} @@ -16,7 +18,7 @@ isoparse({{ source }}) {{ destination }} = {{ source }}.isoformat() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, str]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} {{ destination }} = {{ source }}.isoformat() if {{ source }} else None 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 2510cf089..4916927bd 100644 --- a/openapi_python_client/templates/property_templates/enum_property.py.jinja +++ b/openapi_python_client/templates/property_templates/enum_property.py.jinja @@ -8,6 +8,8 @@ {{ construct_template(construct_function, property, source, initial_value=initial_value) }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.value_type.__name__ }}){% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {% if property.required %} {% if property.nullable %} @@ -16,12 +18,12 @@ {{ destination }} = {{ source }}.value {% endif %} {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} - {{ destination }} = {{ source }} if {{ source }} else None + {{ destination }} = {{ source }}.value if {{ source }} else None {% else %} - {{ destination }} = {{ source }} + {{ destination }} = {{ source }}.value {% endif %} {% endif %} {% 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 6a27349ff..f8fd0c193 100644 --- a/openapi_python_client/templates/property_templates/file_property.py.jinja +++ b/openapi_python_client/templates/property_templates/file_property.py.jinja @@ -10,6 +10,8 @@ File( {{ construct_template(construct_function, property, source, initial_value=initial_value) }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, bytes){% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {% if property.required %} {% if property.nullable %} @@ -18,7 +20,7 @@ File( {{ destination }} = {{ source }}.to_tuple() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} {{ destination }} = {{ source }}.to_tuple() if {{ source }} else None 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 3a065864b..c6ef85254 100644 --- a/openapi_python_client/templates/property_templates/list_property.py.jinja +++ b/openapi_python_client/templates/property_templates/list_property.py.jinja @@ -31,6 +31,7 @@ for {{ inner_source }} in {{ source }}: {% endif %} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, list){% endmacro %} {% macro transform(property, source, destination, declare_type=True) %} {% set inner_property = property.inner_property %} @@ -44,13 +45,13 @@ else: {{ _transform(property, source, destination) }} {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, List[Any]]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} if {{ source }} is None: {{ destination }} = None else: - {{ _transform(property, source, destination) | indent(4)}} + {{ _transform(property, source, destination) | indent(8)}} {% else %} {{ _transform(property, source, destination) | indent(4)}} {% endif %} 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 ebd436704..4e394cc34 100644 --- a/openapi_python_client/templates/property_templates/model_property.py.jinja +++ b/openapi_python_client/templates/property_templates/model_property.py.jinja @@ -8,6 +8,8 @@ {{ construct_template(construct_function, property, source, initial_value=initial_value) }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, dict){% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {% if property.required %} {% if property.nullable %} @@ -16,7 +18,7 @@ {{ destination }} = {{ source }}.to_dict() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[{% if property.nullable %}None, {% endif %}Unset, Dict[str, Any]]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): {% if property.nullable %} {{ destination }} = {{ source }}.to_dict() if {{ source }} else None diff --git a/openapi_python_client/templates/property_templates/none_property.py.jinja b/openapi_python_client/templates/property_templates/none_property.py.jinja index b3178780a..adc6b1524 100644 --- a/openapi_python_client/templates/property_templates/none_property.py.jinja +++ b/openapi_python_client/templates/property_templates/none_property.py.jinja @@ -2,6 +2,8 @@ {{ property.python_name }} = {{ initial_value }} {% endmacro %} +{% macro check_type_for_construct(property, source) %}{{ source }} is None{% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} {{ destination }} = None {% endmacro %} 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 4c632c60a..684ae942d 100644 --- a/openapi_python_client/templates/property_templates/union_property.py.jinja +++ b/openapi_python_client/templates/property_templates/union_property.py.jinja @@ -1,19 +1,31 @@ {% macro construct(property, source, initial_value=None) %} -def _parse_{{ property.python_name }}(data: Any) -> {{ property.get_type_string() }}: - data = None if isinstance(data, Unset) else data - {{ property.python_name }}: {{ property.get_type_string() }} +def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}: + {% if "None" in property.get_type_strings_in_union(json=True) %} + if data is None: + return data + {% endif %} + {% if "Unset" in property.get_type_strings_in_union(json=True) %} + if isinstance(data, Unset): + return data + {% endif %} {% for inner_property in property.inner_properties_with_template() %} {% if not loop.last or property.has_properties_without_templates %} try: - {% from "property_templates/" + inner_property.template import construct %} + {{ inner_property.python_name }}: {{ inner_property.get_type_string() }} + {% from "property_templates/" + inner_property.template import construct, check_type_for_construct %} + if not {{ check_type_for_construct(inner_property, "data") }}: + raise TypeError() {{ construct(inner_property, "data", initial_value="UNSET") | indent(8) }} - return {{ property.python_name }} + return {{ inner_property.python_name }} except: # noqa: E722 pass {% else %}{# Don't do try/except for the last one #} - {% from "property_templates/" + inner_property.template import construct %} + {% from "property_templates/" + inner_property.template import construct, check_type_for_construct %} + if not {{ check_type_for_construct(inner_property, "data") }}: + raise TypeError() + {{ inner_property.python_name }}: {{ inner_property.get_type_string() }} {{ construct(inner_property, "data", initial_value="UNSET") | indent(4) }} - return {{ property.python_name }} + return {{ inner_property.python_name }} {% endif %} {% endfor %} {% if property.has_properties_without_templates %} @@ -24,20 +36,25 @@ def _parse_{{ property.python_name }}(data: Any) -> {{ property.get_type_string( {{ property.python_name }} = _parse_{{ property.python_name }}({{ source }}) {% endmacro %} +{# For now we assume there will be no unions of unions #} +{% macro check_type_for_construct(property, source) %}True{% endmacro %} + {% macro transform(property, source, destination, declare_type=True) %} -{% if not property.required %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} +{% if not property.required or property.nullable %} +{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} +{% if not property.required %} if isinstance({{ source }}, Unset): {{ destination }} = UNSET {% endif %} +{% endif %} {% if property.nullable %} {% if property.required %} if {{ source }} is None: {% else %}{# There's an if UNSET statement before this #} elif {{ source }} is None: {% endif %} - {{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = None + {{ destination }} = None {% endif %} {% for inner_property in property.inner_properties_with_template() %} {% if loop.first and property.required and not property.nullable %}{# No if UNSET or if None statement before this #} diff --git a/openapi_python_client/templates/types.py.jinja b/openapi_python_client/templates/types.py.jinja index 2061b9f08..116054226 100644 --- a/openapi_python_client/templates/types.py.jinja +++ b/openapi_python_client/templates/types.py.jinja @@ -11,6 +11,9 @@ class Unset: UNSET: Unset = Unset() +{# Used as `FileProperty._json_type_string` #} +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +23,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index ee07d3973..03515ea63 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -16,45 +16,51 @@ class TestProperty: - def test_get_type_string(self, mocker): + @pytest.mark.parametrize( + "nullable,required,no_optional,json,expected", + [ + (False, False, False, False, "Union[Unset, TestType]"), + (False, False, True, False, "TestType"), + (False, True, False, False, "TestType"), + (False, True, True, False, "TestType"), + (True, False, False, False, "Union[Unset, None, TestType]"), + (True, False, True, False, "TestType"), + (True, True, False, False, "Optional[TestType]"), + (True, True, True, False, "TestType"), + (False, False, False, True, "Union[Unset, str]"), + (False, False, True, True, "str"), + (False, True, False, True, "str"), + (False, True, True, True, "str"), + (True, False, False, True, "Union[Unset, None, str]"), + (True, False, True, True, "str"), + (True, True, False, True, "Optional[str]"), + (True, True, True, True, "str"), + ], + ) + def test_get_type_string(self, mocker, nullable, required, no_optional, json, expected): from openapi_python_client.parser.properties import Property mocker.patch.object(Property, "_type_string", "TestType") - p = Property(name="test", required=True, default=None, nullable=False) - - base_type_string = f"TestType" - - assert p.get_type_string() == base_type_string + mocker.patch.object(Property, "_json_type_string", "str") + p = Property(name="test", required=required, default=None, nullable=nullable) + assert p.get_type_string(no_optional=no_optional, json=json) == expected - p = Property(name="test", required=True, default=None, nullable=True) - assert p.get_type_string() == f"Optional[{base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = Property(name="test", required=False, default=None, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = Property(name="test", required=False, default=None, nullable=False) - assert p.get_type_string() == f"Union[Unset, {base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - def test_to_string(self, mocker): + @pytest.mark.parametrize( + "default,required,expected", + [ + (None, False, "test: Union[Unset, TestType] = UNSET"), + (None, True, "test: TestType"), + ("Test", False, "test: Union[Unset, TestType] = Test"), + ("Test", True, "test: TestType = Test"), + ], + ) + def test_to_string(self, mocker, default, required, expected): from openapi_python_client.parser.properties import Property name = "test" - get_type_string = mocker.patch.object(Property, "get_type_string") - p = Property(name=name, required=True, default=None, nullable=False) - - assert p.to_string() == f"{name}: {get_type_string()}" - - p = Property(name=name, required=False, default=None, nullable=False) - assert p.to_string() == f"{name}: {get_type_string()} = UNSET" - - p = Property(name=name, required=True, default=None, nullable=False) - assert p.to_string() == f"{name}: {get_type_string()}" - - p = Property(name=name, required=True, default="TEST", nullable=False) - assert p.to_string() == f"{name}: {get_type_string()} = TEST" + mocker.patch.object(Property, "_type_string", "TestType") + p = Property(name=name, required=required, default=default, nullable=False) + assert p.to_string() == expected def test_get_imports(self): from openapi_python_client.parser.properties import Property @@ -82,12 +88,13 @@ def test_get_type_string(self): base_type_string = f"str" assert p.get_type_string() == base_type_string + assert p.get_type_string(json=True) == base_type_string p = StringProperty(name="test", required=True, default=None, nullable=True) assert p.get_type_string() == f"Optional[{base_type_string}]" p = StringProperty(name="test", required=False, default=None, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" p = StringProperty(name="test", required=False, default=None, nullable=False) assert p.get_type_string() == f"Union[Unset, {base_type_string}]" @@ -190,19 +197,22 @@ def test_get_type_string(self, mocker): inner_property = mocker.MagicMock() inner_type_string = mocker.MagicMock() - inner_property.get_type_string.return_value = inner_type_string + inner_property.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "int" if json else inner_type_string + ) p = ListProperty(name="test", required=True, default=None, inner_property=inner_property, nullable=False) base_type_string = f"List[{inner_type_string}]" assert p.get_type_string() == base_type_string + assert p.get_type_string(json=True) == "List[int]" p = ListProperty(name="test", required=True, default=None, inner_property=inner_property, nullable=True) assert p.get_type_string() == f"Optional[{base_type_string}]" assert p.get_type_string(no_optional=True) == base_type_string p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" assert p.get_type_string(no_optional=True) == base_type_string p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=False) @@ -242,55 +252,102 @@ def test_get_type_imports(self, mocker): class TestUnionProperty: - def test_get_type_string(self, mocker): + @pytest.mark.parametrize( + "nullable,required,no_optional,json,expected", + [ + (False, False, False, False, "Union[Unset, inner_type_string_1, inner_type_string_2]"), + (False, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (False, True, False, False, "Union[inner_type_string_1, inner_type_string_2]"), + (False, True, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (True, False, False, False, "Union[None, Unset, inner_type_string_1, inner_type_string_2]"), + (True, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (True, True, False, False, "Union[None, inner_type_string_1, inner_type_string_2]"), + (True, True, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (False, False, False, True, "Union[Unset, inner_json_type_string_1, inner_json_type_string_2]"), + (False, False, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + (False, True, False, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + (False, True, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + (True, False, False, True, "Union[None, Unset, inner_json_type_string_1, inner_json_type_string_2]"), + (True, False, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + (True, True, False, True, "Union[None, inner_json_type_string_1, inner_json_type_string_2]"), + (True, True, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + ], + ) + def test_get_type_string(self, mocker, nullable, required, no_optional, json, expected): from openapi_python_client.parser.properties import UnionProperty inner_property_1 = mocker.MagicMock() - inner_property_1.get_type_string.return_value = "inner_type_string_1" + inner_property_1.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" + ) inner_property_2 = mocker.MagicMock() - inner_property_2.get_type_string.return_value = "inner_type_string_2" + inner_property_2.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" + ) p = UnionProperty( name="test", - required=True, + required=required, default=None, inner_properties=[inner_property_1, inner_property_2], - nullable=False, + nullable=nullable, ) + assert p.get_type_string(no_optional=no_optional, json=json) == expected - base_type_string = f"Union[inner_type_string_1, inner_type_string_2]" - - assert p.get_type_string() == base_type_string + def test_get_base_type_string(self, mocker): + from openapi_python_client.parser.properties import UnionProperty + inner_property_1 = mocker.MagicMock() + inner_property_1.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" + ) + inner_property_2 = mocker.MagicMock() + inner_property_2.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" + ) p = UnionProperty( name="test", - required=True, + required=False, default=None, inner_properties=[inner_property_1, inner_property_2], nullable=True, ) - assert p.get_type_string() == f"Optional[{base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string + assert p.get_base_type_string() == "Union[inner_type_string_1, inner_type_string_2]" + + def test_get_base_type_string_one_element(self, mocker): + from openapi_python_client.parser.properties import UnionProperty - base_type_string_with_unset = f"Union[Unset, inner_type_string_1, inner_type_string_2]" + inner_property_1 = mocker.MagicMock() + inner_property_1.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" + ) p = UnionProperty( name="test", required=False, default=None, - inner_properties=[inner_property_1, inner_property_2], + inner_properties=[inner_property_1], nullable=True, ) - assert p.get_type_string() == f"Optional[{base_type_string_with_unset}]" - assert p.get_type_string(no_optional=True) == base_type_string + assert p.get_base_type_string() == "inner_type_string_1" + + def test_get_base_json_type_string(self, mocker): + from openapi_python_client.parser.properties import UnionProperty + inner_property_1 = mocker.MagicMock() + inner_property_1.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" + ) + inner_property_2 = mocker.MagicMock() + inner_property_2.get_type_string.side_effect = ( + lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" + ) p = UnionProperty( name="test", required=False, default=None, inner_properties=[inner_property_1, inner_property_2], - nullable=False, + nullable=True, ) - assert p.get_type_string() == base_type_string_with_unset - assert p.get_type_string(no_optional=True) == base_type_string + assert p.get_base_json_type_string() == "Union[inner_json_type_string_1, inner_json_type_string_2]" def test_get_imports(self, mocker): from openapi_python_client.parser.properties import UnionProperty @@ -367,6 +424,7 @@ def test_get_type_string(self, mocker): base_type_string = f"MyTestEnum" assert p.get_type_string() == base_type_string + assert p.get_type_string(json=True) == "str" p = properties.EnumProperty( name="test", @@ -389,7 +447,7 @@ def test_get_type_string(self, mocker): reference=fake_reference, value_type=str, ) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" assert p.get_type_string(no_optional=True) == base_type_string p = properties.EnumProperty( diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 1024ef179..def1734e7 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -2,19 +2,20 @@ @pytest.mark.parametrize( - "no_optional,nullable,required,expected", + "no_optional,nullable,required,json,expected", [ - (False, False, False, "Union[MyClass, Unset]"), - (False, False, True, "MyClass"), - (False, True, False, "Union[Optional[MyClass], Unset]"), - (False, True, True, "Optional[MyClass]"), - (True, False, False, "MyClass"), - (True, False, True, "MyClass"), - (True, True, False, "MyClass"), - (True, True, True, "MyClass"), + (False, False, False, False, "Union[Unset, MyClass]"), + (False, False, True, False, "MyClass"), + (False, True, False, False, "Union[Unset, None, MyClass]"), + (False, True, True, False, "Optional[MyClass]"), + (True, False, False, False, "MyClass"), + (True, False, True, False, "MyClass"), + (True, True, False, False, "MyClass"), + (True, True, True, False, "MyClass"), + (False, False, True, True, "Dict[str, Any]"), ], ) -def test_get_type_string(no_optional, nullable, required, expected): +def test_get_type_string(no_optional, nullable, required, json, expected): from openapi_python_client.parser.properties import ModelProperty, Reference prop = ModelProperty( @@ -30,7 +31,7 @@ def test_get_type_string(no_optional, nullable, required, expected): additional_properties=False, ) - assert prop.get_type_string(no_optional=no_optional) == expected + assert prop.get_type_string(no_optional=no_optional, json=json) == expected def test_get_imports(): diff --git a/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py b/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py index bdbe148c1..0b44852df 100644 --- a/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py +++ b/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py @@ -3,7 +3,7 @@ from dateutil.parser import isoparse some_source = date(2020, 10, 12) -some_destination: Union[Unset, str] = UNSET +some_destination: Union[Unset, None, str] = UNSET if not isinstance(some_source, Unset): some_destination = some_source.isoformat() if some_source else None