Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bad code generated for nested unions #959

Merged
merged 4 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/fix_invalid_type_check_for_nested_unions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
default: patch
---

# Fix invalid type check for nested unions

Nested union types (unions of unions) were generating `isinstance()` checks that were not valid (at least for Python 3.9).

Thanks to @codebutler for PR #959 which fixes #958 and #967.
49 changes: 48 additions & 1 deletion end_to_end_tests/baseline_openapi_3.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@
}
}
}
},
},
"/enum/int": {
"post": {
"tags": [
Expand Down Expand Up @@ -2531,6 +2531,53 @@
"ModelWithBackslashInDescription": {
"type": "object",
"description": "Description with special character: \\"
},
"ModelWithDiscriminatedUnion": {
"type": "object",
"properties": {
"discriminated_union": {
"allOf": [
{
"$ref": "#/components/schemas/ADiscriminatedUnion"
}
],
"nullable": true
}
}
},
"ADiscriminatedUnion": {
"type": "object",
"discriminator": {
"propertyName": "modelType",
"mapping": {
"type1": "#/components/schemas/ADiscriminatedUnionType1",
"type2": "#/components/schemas/ADiscriminatedUnionType2"
}
},
"oneOf": [
{
"$ref": "#/components/schemas/ADiscriminatedUnionType1"
},
{
"$ref": "#/components/schemas/ADiscriminatedUnionType2"
}
]
},
"ADiscriminatedUnionType1": {
"type": "object",
"properties": {
"modelType": {
"type": "string"
}
}
},
"ADiscriminatedUnionType2": {
"type": "object",
"properties": {
"modelType": {
"type": "string"
}
}
}
},
"parameters": {
Expand Down
49 changes: 49 additions & 0 deletions end_to_end_tests/baseline_openapi_3.1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2543,6 +2543,55 @@ info:
"ModelWithBackslashInDescription": {
"type": "object",
"description": "Description with special character: \\"
},
"ModelWithDiscriminatedUnion": {
"type": "object",
"properties": {
"discriminated_union": {
"oneOf": [
{
"$ref": "#/components/schemas/ADiscriminatedUnion"
},
{
"type": "null"
}
],
}
}
},
"ADiscriminatedUnion": {
"type": "object",
"discriminator": {
"propertyName": "modelType",
"mapping": {
"type1": "#/components/schemas/ADiscriminatedUnionType1",
"type2": "#/components/schemas/ADiscriminatedUnionType2"
}
},
"oneOf": [
{
"$ref": "#/components/schemas/ADiscriminatedUnionType1"
},
{
"$ref": "#/components/schemas/ADiscriminatedUnionType2"
}
]
},
"ADiscriminatedUnionType1": {
"type": "object",
"properties": {
"modelType": {
"type": "string"
}
}
},
"ADiscriminatedUnionType2": {
"type": "object",
"properties": {
"modelType": {
"type": "string"
}
}
}
},
"parameters": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
""" Contains all the data models used in inputs/outputs """

from .a_discriminated_union_type_1 import ADiscriminatedUnionType1
from .a_discriminated_union_type_2 import ADiscriminatedUnionType2
from .a_form_data import AFormData
from .a_model import AModel
from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject
Expand Down Expand Up @@ -54,6 +56,7 @@
from .model_with_circular_ref_in_additional_properties_a import ModelWithCircularRefInAdditionalPropertiesA
from .model_with_circular_ref_in_additional_properties_b import ModelWithCircularRefInAdditionalPropertiesB
from .model_with_date_time_property import ModelWithDateTimeProperty
from .model_with_discriminated_union import ModelWithDiscriminatedUnion
from .model_with_primitive_additional_properties import ModelWithPrimitiveAdditionalProperties
from .model_with_primitive_additional_properties_a_date_holder import ModelWithPrimitiveAdditionalPropertiesADateHolder
from .model_with_property_ref import ModelWithPropertyRef
Expand All @@ -79,6 +82,8 @@
from .validation_error import ValidationError

__all__ = (
"ADiscriminatedUnionType1",
"ADiscriminatedUnionType2",
"AFormData",
"AllOfHasPropertiesButNoType",
"AllOfHasPropertiesButNoTypeTypeEnum",
Expand Down Expand Up @@ -125,6 +130,7 @@
"ModelWithCircularRefInAdditionalPropertiesA",
"ModelWithCircularRefInAdditionalPropertiesB",
"ModelWithDateTimeProperty",
"ModelWithDiscriminatedUnion",
"ModelWithPrimitiveAdditionalProperties",
"ModelWithPrimitiveAdditionalPropertiesADateHolder",
"ModelWithPropertyRef",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from typing import Any, Dict, List, Type, TypeVar, Union

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

T = TypeVar("T", bound="ADiscriminatedUnionType1")


@_attrs_define
class ADiscriminatedUnionType1:
"""
Attributes:
model_type (Union[Unset, str]):
"""

model_type: Union[Unset, str] = UNSET
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
model_type = self.model_type

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if model_type is not UNSET:
field_dict["modelType"] = model_type

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
model_type = d.pop("modelType", UNSET)

a_discriminated_union_type_1 = cls(
model_type=model_type,
)

a_discriminated_union_type_1.additional_properties = d
return a_discriminated_union_type_1

@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
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from typing import Any, Dict, List, Type, TypeVar, Union

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

T = TypeVar("T", bound="ADiscriminatedUnionType2")


@_attrs_define
class ADiscriminatedUnionType2:
"""
Attributes:
model_type (Union[Unset, str]):
"""

model_type: Union[Unset, str] = UNSET
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
model_type = self.model_type

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if model_type is not UNSET:
field_dict["modelType"] = model_type

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
model_type = d.pop("modelType", UNSET)

a_discriminated_union_type_2 = cls(
model_type=model_type,
)

a_discriminated_union_type_2.additional_properties = d
return a_discriminated_union_type_2

@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
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union, cast

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

if TYPE_CHECKING:
from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1
from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2


T = TypeVar("T", bound="ModelWithDiscriminatedUnion")


@_attrs_define
class ModelWithDiscriminatedUnion:
"""
Attributes:
discriminated_union (Union['ADiscriminatedUnionType1', 'ADiscriminatedUnionType2', None, Unset]):
"""

discriminated_union: Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset] = UNSET
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1
from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2

discriminated_union: Union[Dict[str, Any], None, Unset]
if isinstance(self.discriminated_union, Unset):
discriminated_union = UNSET
elif isinstance(self.discriminated_union, ADiscriminatedUnionType1):
discriminated_union = self.discriminated_union.to_dict()
elif isinstance(self.discriminated_union, ADiscriminatedUnionType2):
discriminated_union = self.discriminated_union.to_dict()
else:
discriminated_union = self.discriminated_union

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
if discriminated_union is not UNSET:
field_dict["discriminated_union"] = discriminated_union

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
from ..models.a_discriminated_union_type_1 import ADiscriminatedUnionType1
from ..models.a_discriminated_union_type_2 import ADiscriminatedUnionType2

d = src_dict.copy()

def _parse_discriminated_union(
data: object,
) -> Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_a_discriminated_union_type_0 = ADiscriminatedUnionType1.from_dict(data)

return componentsschemas_a_discriminated_union_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, dict):
raise TypeError()
componentsschemas_a_discriminated_union_type_1 = ADiscriminatedUnionType2.from_dict(data)

return componentsschemas_a_discriminated_union_type_1
except: # noqa: E722
pass
return cast(Union["ADiscriminatedUnionType1", "ADiscriminatedUnionType2", None, Unset], data)

discriminated_union = _parse_discriminated_union(d.pop("discriminated_union", UNSET))

model_with_discriminated_union = cls(
discriminated_union=discriminated_union,
)

model_with_discriminated_union.additional_properties = d
return model_with_discriminated_union

@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
Loading