Skip to content

fix(parser): indirect and recursive reference resolution (v2) #419

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

Closed
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
""" Contains all the data models used in inputs/outputs """

from .a_model import AModel
from .a_model_with_direct_self_reference_property import AModelWithDirectSelfReferenceProperty
from .a_model_with_indirect_reference_property import AModelWithIndirectReferenceProperty
from .a_model_with_indirect_self_reference_property import AModelWithIndirectSelfReferenceProperty
from .all_of_sub_model import AllOfSubModel
from .an_all_of_enum import AnAllOfEnum
from .an_enum import AnEnum
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import Any, Dict, List, Type, TypeVar, Union

import attr

from ..types import UNSET, Unset

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


@attr.s(auto_attribs=True)
class AModelWithDirectSelfReferenceProperty:
""" """

required_self_ref: AModelWithDirectSelfReferenceProperty
optional_self_ref: Union[Unset, AModelWithDirectSelfReferenceProperty] = UNSET
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

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

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

return field_dict

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

optional_self_ref = d.pop("optional_self_ref", UNSET)

a_model_with_direct_self_reference_property = cls(
required_self_ref=required_self_ref,
optional_self_ref=optional_self_ref,
)

a_model_with_direct_self_reference_property.additional_properties = d
return a_model_with_direct_self_reference_property

@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,60 @@
from typing import Any, Dict, List, Type, TypeVar, Union

import attr

from ..models.an_enum import AnEnum
from ..types import UNSET, Unset

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


@attr.s(auto_attribs=True)
class AModelWithIndirectReferenceProperty:
""" """

an_enum_indirect_ref: Union[Unset, AnEnum] = UNSET
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
an_enum_indirect_ref: Union[Unset, str] = UNSET
if not isinstance(self.an_enum_indirect_ref, Unset):
an_enum_indirect_ref = self.an_enum_indirect_ref.value

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

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
an_enum_indirect_ref: Union[Unset, AnEnum] = UNSET
_an_enum_indirect_ref = d.pop("an_enum_indirect_ref", UNSET)
if not isinstance(_an_enum_indirect_ref, Unset):
an_enum_indirect_ref = AnEnum(_an_enum_indirect_ref)

a_model_with_indirect_reference_property = cls(
an_enum_indirect_ref=an_enum_indirect_ref,
)

a_model_with_indirect_reference_property.additional_properties = d
return a_model_with_indirect_reference_property

@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,77 @@
from typing import Any, Dict, List, Type, TypeVar, Union

import attr

from ..models.an_enum import AnEnum
from ..types import UNSET, Unset

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


@attr.s(auto_attribs=True)
class AModelWithIndirectSelfReferenceProperty:
""" """

required_self_ref: AModelWithIndirectSelfReferenceProperty
an_enum: Union[Unset, AnEnum] = UNSET
optional_self_ref: Union[Unset, AModelWithIndirectSelfReferenceProperty] = UNSET
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
required_self_ref = self.required_self_ref
an_enum: Union[Unset, str] = UNSET
if not isinstance(self.an_enum, Unset):
an_enum = self.an_enum.value

optional_self_ref = self.optional_self_ref

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

return field_dict

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

an_enum: Union[Unset, AnEnum] = UNSET
_an_enum = d.pop("an_enum", UNSET)
if not isinstance(_an_enum, Unset):
an_enum = AnEnum(_an_enum)

optional_self_ref = d.pop("optional_self_ref", UNSET)

a_model_with_indirect_self_reference_property = cls(
required_self_ref=required_self_ref,
an_enum=an_enum,
optional_self_ref=optional_self_ref,
)

a_model_with_indirect_self_reference_property.additional_properties = d
return a_model_with_indirect_self_reference_property

@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
52 changes: 52 additions & 0 deletions end_to_end_tests/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,58 @@
"properties": {
"inner": {"$ref": "#/components/schemas/model_reference_doesnt_match"}
}
},
"AModelWithIndirectReferenceProperty": {
"title": "AModelWithIndirectReferenceProperty",
"type": "object",
"properties": {
"an_enum_indirect_ref": {
"$ref": "#/components/schemas/AnEnumDeeperIndirectReference"
}
}
},
"AnEnumDeeperIndirectReference": {
"$ref": "#/components/schemas/AnEnumIndirectReference"
},
"AnEnumIndirectReference": {
"$ref": "#/components/schemas/AnEnum"
},
"AModelWithDirectSelfReferenceProperty": {
"type": "object",
"properties": {
"required_self_ref": {
"$ref": "#/components/schemas/AModelWithDirectSelfReferenceProperty",
},
"optional_self_ref": {
"$ref": "#/components/schemas/AModelWithDirectSelfReferenceProperty",
}
},
"required": [
"required_self_ref"
]
},
"AModelWithIndirectSelfReferenceProperty": {
"type": "object",
"properties": {
"an_enum": {
"$ref": "#/components/schemas/AnEnum"
},
"required_self_ref": {
"$ref": "#/components/schemas/AnDeeperIndirectSelfReference"
},
"optional_self_ref": {
"$ref": "#/components/schemas/AnDeeperIndirectSelfReference"
}
},
"required": [
"required_self_ref"
]
},
"AnDeeperIndirectSelfReference": {
"$ref": "#/components/schemas/AnIndirectSelfReference"
},
"AnIndirectSelfReference": {
"$ref": "#/components/schemas/AModelWithIndirectSelfReferenceProperty"
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion openapi_python_client/parser/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from enum import Enum
from typing import Optional
from typing import Any, Optional

__all__ = ["ErrorLevel", "GeneratorError", "ParseError", "PropertyError", "ValidationError"]

Expand Down Expand Up @@ -39,5 +39,12 @@ class PropertyError(ParseError):
header = "Problem creating a Property: "


@dataclass
class RecursiveReferenceInterupt(PropertyError):
"""Error raised when a property have an recursive reference to itself"""

schemas: Optional[Any] = None # TODO: shall not use Any here, shall be Schemas, to fix later


class ValidationError(Exception):
pass
Binary file not shown.
Loading