Skip to content

Commit 4268777

Browse files
committed
Add support for recursive/circular refs
This commit adds support for recursive and circular references in object properties and additionalProperties, but not in allOf. The changes include: -Delayed processing of schema model properties -Cascading removal of invalid schema reference dependencies -Prevention of self import in ModelProperty relative imports -Prevention of forward recursive type reference errors in generated modules -Logging for detection of recursive references in allOf
1 parent 65cf25b commit 4268777

16 files changed

+1178
-125
lines changed

Diff for: end_to_end_tests/golden-record/my_test_api_client/models/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@
3131
from .model_with_additional_properties_refed import ModelWithAdditionalPropertiesRefed
3232
from .model_with_any_json_properties import ModelWithAnyJsonProperties
3333
from .model_with_any_json_properties_additional_property_type_0 import ModelWithAnyJsonPropertiesAdditionalPropertyType0
34+
from .model_with_circular_ref_a import ModelWithCircularRefA
35+
from .model_with_circular_ref_b import ModelWithCircularRefB
36+
from .model_with_circular_ref_in_additional_properties_a import ModelWithCircularRefInAdditionalPropertiesA
37+
from .model_with_circular_ref_in_additional_properties_b import ModelWithCircularRefInAdditionalPropertiesB
3438
from .model_with_date_time_property import ModelWithDateTimeProperty
3539
from .model_with_primitive_additional_properties import ModelWithPrimitiveAdditionalProperties
3640
from .model_with_primitive_additional_properties_a_date_holder import ModelWithPrimitiveAdditionalPropertiesADateHolder
3741
from .model_with_property_ref import ModelWithPropertyRef
42+
from .model_with_recursive_ref import ModelWithRecursiveRef
43+
from .model_with_recursive_ref_in_additional_properties import ModelWithRecursiveRefInAdditionalProperties
3844
from .model_with_union_property import ModelWithUnionProperty
3945
from .model_with_union_property_inlined import ModelWithUnionPropertyInlined
4046
from .model_with_union_property_inlined_fruit_type_0 import ModelWithUnionPropertyInlinedFruitType0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from typing import Any, Dict, List, Type, TypeVar, Union
2+
3+
import attr
4+
5+
from ..models.model_with_circular_ref_b import ModelWithCircularRefB
6+
from ..types import UNSET, Unset
7+
8+
T = TypeVar("T", bound="ModelWithCircularRefA")
9+
10+
11+
@attr.s(auto_attribs=True)
12+
class ModelWithCircularRefA:
13+
"""
14+
Attributes:
15+
circular (Union[Unset, ModelWithCircularRefB]):
16+
"""
17+
18+
circular: Union[Unset, ModelWithCircularRefB] = UNSET
19+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
20+
21+
def to_dict(self) -> Dict[str, Any]:
22+
circular: Union[Unset, Dict[str, Any]] = UNSET
23+
if not isinstance(self.circular, Unset):
24+
circular = self.circular.to_dict()
25+
26+
field_dict: Dict[str, Any] = {}
27+
field_dict.update(self.additional_properties)
28+
field_dict.update({})
29+
if circular is not UNSET:
30+
field_dict["circular"] = circular
31+
32+
return field_dict
33+
34+
@classmethod
35+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
36+
d = src_dict.copy()
37+
_circular = d.pop("circular", UNSET)
38+
circular: Union[Unset, ModelWithCircularRefB]
39+
if isinstance(_circular, Unset):
40+
circular = UNSET
41+
else:
42+
circular = ModelWithCircularRefB.from_dict(_circular)
43+
44+
model_with_circular_ref_a = cls(
45+
circular=circular,
46+
)
47+
48+
model_with_circular_ref_a.additional_properties = d
49+
return model_with_circular_ref_a
50+
51+
@property
52+
def additional_keys(self) -> List[str]:
53+
return list(self.additional_properties.keys())
54+
55+
def __getitem__(self, key: str) -> Any:
56+
return self.additional_properties[key]
57+
58+
def __setitem__(self, key: str, value: Any) -> None:
59+
self.additional_properties[key] = value
60+
61+
def __delitem__(self, key: str) -> None:
62+
del self.additional_properties[key]
63+
64+
def __contains__(self, key: str) -> bool:
65+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from typing import Any, Dict, List, Type, TypeVar, Union
2+
3+
import attr
4+
5+
from ..models.model_with_circular_ref_a import ModelWithCircularRefA
6+
from ..types import UNSET, Unset
7+
8+
T = TypeVar("T", bound="ModelWithCircularRefB")
9+
10+
11+
@attr.s(auto_attribs=True)
12+
class ModelWithCircularRefB:
13+
"""
14+
Attributes:
15+
circular (Union[Unset, ModelWithCircularRefA]):
16+
"""
17+
18+
circular: Union[Unset, ModelWithCircularRefA] = UNSET
19+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
20+
21+
def to_dict(self) -> Dict[str, Any]:
22+
circular: Union[Unset, Dict[str, Any]] = UNSET
23+
if not isinstance(self.circular, Unset):
24+
circular = self.circular.to_dict()
25+
26+
field_dict: Dict[str, Any] = {}
27+
field_dict.update(self.additional_properties)
28+
field_dict.update({})
29+
if circular is not UNSET:
30+
field_dict["circular"] = circular
31+
32+
return field_dict
33+
34+
@classmethod
35+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
36+
d = src_dict.copy()
37+
_circular = d.pop("circular", UNSET)
38+
circular: Union[Unset, ModelWithCircularRefA]
39+
if isinstance(_circular, Unset):
40+
circular = UNSET
41+
else:
42+
circular = ModelWithCircularRefA.from_dict(_circular)
43+
44+
model_with_circular_ref_b = cls(
45+
circular=circular,
46+
)
47+
48+
model_with_circular_ref_b.additional_properties = d
49+
return model_with_circular_ref_b
50+
51+
@property
52+
def additional_keys(self) -> List[str]:
53+
return list(self.additional_properties.keys())
54+
55+
def __getitem__(self, key: str) -> Any:
56+
return self.additional_properties[key]
57+
58+
def __setitem__(self, key: str, value: Any) -> None:
59+
self.additional_properties[key] = value
60+
61+
def __delitem__(self, key: str) -> None:
62+
del self.additional_properties[key]
63+
64+
def __contains__(self, key: str) -> bool:
65+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
from ..models.model_with_circular_ref_in_additional_properties_b import ModelWithCircularRefInAdditionalPropertiesB
6+
7+
T = TypeVar("T", bound="ModelWithCircularRefInAdditionalPropertiesA")
8+
9+
10+
@attr.s(auto_attribs=True)
11+
class ModelWithCircularRefInAdditionalPropertiesA:
12+
""" """
13+
14+
additional_properties: Dict[str, ModelWithCircularRefInAdditionalPropertiesB] = attr.ib(init=False, factory=dict)
15+
16+
def to_dict(self) -> Dict[str, Any]:
17+
18+
field_dict: Dict[str, Any] = {}
19+
for prop_name, prop in self.additional_properties.items():
20+
field_dict[prop_name] = prop.to_dict()
21+
22+
field_dict.update({})
23+
24+
return field_dict
25+
26+
@classmethod
27+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
28+
d = src_dict.copy()
29+
model_with_circular_ref_in_additional_properties_a = cls()
30+
31+
additional_properties = {}
32+
for prop_name, prop_dict in d.items():
33+
additional_property = ModelWithCircularRefInAdditionalPropertiesB.from_dict(prop_dict)
34+
35+
additional_properties[prop_name] = additional_property
36+
37+
model_with_circular_ref_in_additional_properties_a.additional_properties = additional_properties
38+
return model_with_circular_ref_in_additional_properties_a
39+
40+
@property
41+
def additional_keys(self) -> List[str]:
42+
return list(self.additional_properties.keys())
43+
44+
def __getitem__(self, key: str) -> ModelWithCircularRefInAdditionalPropertiesB:
45+
return self.additional_properties[key]
46+
47+
def __setitem__(self, key: str, value: ModelWithCircularRefInAdditionalPropertiesB) -> None:
48+
self.additional_properties[key] = value
49+
50+
def __delitem__(self, key: str) -> None:
51+
del self.additional_properties[key]
52+
53+
def __contains__(self, key: str) -> bool:
54+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
from ..models.model_with_circular_ref_in_additional_properties_a import ModelWithCircularRefInAdditionalPropertiesA
6+
7+
T = TypeVar("T", bound="ModelWithCircularRefInAdditionalPropertiesB")
8+
9+
10+
@attr.s(auto_attribs=True)
11+
class ModelWithCircularRefInAdditionalPropertiesB:
12+
""" """
13+
14+
additional_properties: Dict[str, ModelWithCircularRefInAdditionalPropertiesA] = attr.ib(init=False, factory=dict)
15+
16+
def to_dict(self) -> Dict[str, Any]:
17+
18+
field_dict: Dict[str, Any] = {}
19+
for prop_name, prop in self.additional_properties.items():
20+
field_dict[prop_name] = prop.to_dict()
21+
22+
field_dict.update({})
23+
24+
return field_dict
25+
26+
@classmethod
27+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
28+
d = src_dict.copy()
29+
model_with_circular_ref_in_additional_properties_b = cls()
30+
31+
additional_properties = {}
32+
for prop_name, prop_dict in d.items():
33+
additional_property = ModelWithCircularRefInAdditionalPropertiesA.from_dict(prop_dict)
34+
35+
additional_properties[prop_name] = additional_property
36+
37+
model_with_circular_ref_in_additional_properties_b.additional_properties = additional_properties
38+
return model_with_circular_ref_in_additional_properties_b
39+
40+
@property
41+
def additional_keys(self) -> List[str]:
42+
return list(self.additional_properties.keys())
43+
44+
def __getitem__(self, key: str) -> ModelWithCircularRefInAdditionalPropertiesA:
45+
return self.additional_properties[key]
46+
47+
def __setitem__(self, key: str, value: ModelWithCircularRefInAdditionalPropertiesA) -> None:
48+
self.additional_properties[key] = value
49+
50+
def __delitem__(self, key: str) -> None:
51+
del self.additional_properties[key]
52+
53+
def __contains__(self, key: str) -> bool:
54+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from typing import Any, Dict, List, Type, TypeVar, Union
2+
3+
import attr
4+
5+
from ..types import UNSET, Unset
6+
7+
T = TypeVar("T", bound="ModelWithRecursiveRef")
8+
9+
10+
@attr.s(auto_attribs=True)
11+
class ModelWithRecursiveRef:
12+
"""
13+
Attributes:
14+
recursive (Union[Unset, ModelWithRecursiveRef]):
15+
"""
16+
17+
recursive: Union[Unset, "ModelWithRecursiveRef"] = UNSET
18+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
19+
20+
def to_dict(self) -> Dict[str, Any]:
21+
recursive: Union[Unset, Dict[str, Any]] = UNSET
22+
if not isinstance(self.recursive, Unset):
23+
recursive = self.recursive.to_dict()
24+
25+
field_dict: Dict[str, Any] = {}
26+
field_dict.update(self.additional_properties)
27+
field_dict.update({})
28+
if recursive is not UNSET:
29+
field_dict["recursive"] = recursive
30+
31+
return field_dict
32+
33+
@classmethod
34+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
35+
d = src_dict.copy()
36+
_recursive = d.pop("recursive", UNSET)
37+
recursive: Union[Unset, ModelWithRecursiveRef]
38+
if isinstance(_recursive, Unset):
39+
recursive = UNSET
40+
else:
41+
recursive = ModelWithRecursiveRef.from_dict(_recursive)
42+
43+
model_with_recursive_ref = cls(
44+
recursive=recursive,
45+
)
46+
47+
model_with_recursive_ref.additional_properties = d
48+
return model_with_recursive_ref
49+
50+
@property
51+
def additional_keys(self) -> List[str]:
52+
return list(self.additional_properties.keys())
53+
54+
def __getitem__(self, key: str) -> Any:
55+
return self.additional_properties[key]
56+
57+
def __setitem__(self, key: str, value: Any) -> None:
58+
self.additional_properties[key] = value
59+
60+
def __delitem__(self, key: str) -> None:
61+
del self.additional_properties[key]
62+
63+
def __contains__(self, key: str) -> bool:
64+
return key in self.additional_properties
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
T = TypeVar("T", bound="ModelWithRecursiveRefInAdditionalProperties")
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class ModelWithRecursiveRefInAdditionalProperties:
10+
""" """
11+
12+
additional_properties: Dict[str, "ModelWithRecursiveRefInAdditionalProperties"] = attr.ib(init=False, factory=dict)
13+
14+
def to_dict(self) -> Dict[str, Any]:
15+
16+
field_dict: Dict[str, Any] = {}
17+
for prop_name, prop in self.additional_properties.items():
18+
field_dict[prop_name] = prop.to_dict()
19+
20+
field_dict.update({})
21+
22+
return field_dict
23+
24+
@classmethod
25+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
26+
d = src_dict.copy()
27+
model_with_recursive_ref_in_additional_properties = cls()
28+
29+
additional_properties = {}
30+
for prop_name, prop_dict in d.items():
31+
additional_property = ModelWithRecursiveRefInAdditionalProperties.from_dict(prop_dict)
32+
33+
additional_properties[prop_name] = additional_property
34+
35+
model_with_recursive_ref_in_additional_properties.additional_properties = additional_properties
36+
return model_with_recursive_ref_in_additional_properties
37+
38+
@property
39+
def additional_keys(self) -> List[str]:
40+
return list(self.additional_properties.keys())
41+
42+
def __getitem__(self, key: str) -> "ModelWithRecursiveRefInAdditionalProperties":
43+
return self.additional_properties[key]
44+
45+
def __setitem__(self, key: str, value: "ModelWithRecursiveRefInAdditionalProperties") -> None:
46+
self.additional_properties[key] = value
47+
48+
def __delitem__(self, key: str) -> None:
49+
del self.additional_properties[key]
50+
51+
def __contains__(self, key: str) -> bool:
52+
return key in self.additional_properties

0 commit comments

Comments
 (0)