Skip to content

Commit 627ec1a

Browse files
benweintmicha91
authored andcommitted
fix: import cast for required const properties, since it's used in the template (openapi-generators#1153)
The [template](https://github.com/openapi-generators/openapi-python-client/blob/main/openapi_python_client/templates/property_templates/const_property.py.jinja#L2) for `const` properties uses `cast` unconditionally, but the import for `cast` in `ConstProperty.get_imports` was conditioned on whether the property was required or not, leading to broken generated code for required const properties. I'd be happy to add a test for this if desired! A cursory glance suggests that maybe the end_to_end tests would be the right place? Resolves openapi-generators#1150
1 parent ad5907c commit 627ec1a

File tree

8 files changed

+399
-2
lines changed

8 files changed

+399
-2
lines changed

end_to_end_tests/baseline_openapi_3.0.json

+41
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,47 @@
17171717
}
17181718
}
17191719
}
1720+
},
1721+
"/models/oneof-with-required-const": {
1722+
"get": {
1723+
"responses": {
1724+
"200": {
1725+
"description": "OK",
1726+
"content": {
1727+
"application/json": {
1728+
"schema": {
1729+
"oneOf": [
1730+
{
1731+
"type": "object",
1732+
"properties": {
1733+
"type": {
1734+
"const": "alpha"
1735+
},
1736+
"color": {
1737+
"type": "string"
1738+
}
1739+
},
1740+
"required": ["type"]
1741+
},
1742+
{
1743+
"type": "object",
1744+
"properties": {
1745+
"type": {
1746+
"const": "beta"
1747+
},
1748+
"texture": {
1749+
"type": "string"
1750+
}
1751+
},
1752+
"required": ["type"]
1753+
}
1754+
]
1755+
}
1756+
}
1757+
}
1758+
}
1759+
}
1760+
}
17201761
}
17211762
},
17221763
"components": {

end_to_end_tests/baseline_openapi_3.1.yaml

+41
Original file line numberDiff line numberDiff line change
@@ -1708,6 +1708,47 @@ info:
17081708
}
17091709
}
17101710
},
1711+
"/models/oneof-with-required-const": {
1712+
"get": {
1713+
"responses": {
1714+
"200": {
1715+
"description": "OK",
1716+
"content": {
1717+
"application/json": {
1718+
"schema": {
1719+
"oneOf": [
1720+
{
1721+
"type": "object",
1722+
"properties": {
1723+
"type": {
1724+
"const": "alpha"
1725+
},
1726+
"color": {
1727+
"type": "string"
1728+
}
1729+
},
1730+
"required": ["type"]
1731+
},
1732+
{
1733+
"type": "object",
1734+
"properties": {
1735+
"type": {
1736+
"const": "beta"
1737+
},
1738+
"texture": {
1739+
"type": "string"
1740+
}
1741+
},
1742+
"required": ["type"]
1743+
}
1744+
]
1745+
}
1746+
}
1747+
}
1748+
}
1749+
}
1750+
}
1751+
}
17111752
}
17121753
"components":
17131754
"schemas": {

end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
import types
44

5-
from . import get_common_parameters, get_models_allof, post_common_parameters, reserved_parameters
5+
from . import (
6+
get_common_parameters,
7+
get_models_allof,
8+
get_models_oneof_with_required_const,
9+
post_common_parameters,
10+
reserved_parameters,
11+
)
612

713

814
class DefaultEndpoints:
@@ -21,3 +27,7 @@ def reserved_parameters(cls) -> types.ModuleType:
2127
@classmethod
2228
def get_models_allof(cls) -> types.ModuleType:
2329
return get_models_allof
30+
31+
@classmethod
32+
def get_models_oneof_with_required_const(cls) -> types.ModuleType:
33+
return get_models_oneof_with_required_const
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
from http import HTTPStatus
2+
from typing import Any, Dict, Optional, Union
3+
4+
import httpx
5+
6+
from ... import errors
7+
from ...client import AuthenticatedClient, Client
8+
from ...models.get_models_oneof_with_required_const_response_200_type_0 import (
9+
GetModelsOneofWithRequiredConstResponse200Type0,
10+
)
11+
from ...models.get_models_oneof_with_required_const_response_200_type_1 import (
12+
GetModelsOneofWithRequiredConstResponse200Type1,
13+
)
14+
from ...types import Response
15+
16+
17+
def _get_kwargs() -> Dict[str, Any]:
18+
_kwargs: Dict[str, Any] = {
19+
"method": "get",
20+
"url": "/models/oneof-with-required-const",
21+
}
22+
23+
return _kwargs
24+
25+
26+
def _parse_response(
27+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
28+
) -> Optional[
29+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
30+
]:
31+
if response.status_code == 200:
32+
33+
def _parse_response_200(
34+
data: object,
35+
) -> Union[
36+
"GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"
37+
]:
38+
try:
39+
if not isinstance(data, dict):
40+
raise TypeError()
41+
response_200_type_0 = GetModelsOneofWithRequiredConstResponse200Type0.from_dict(data)
42+
43+
return response_200_type_0
44+
except: # noqa: E722
45+
pass
46+
if not isinstance(data, dict):
47+
raise TypeError()
48+
response_200_type_1 = GetModelsOneofWithRequiredConstResponse200Type1.from_dict(data)
49+
50+
return response_200_type_1
51+
52+
response_200 = _parse_response_200(response.json())
53+
54+
return response_200
55+
if client.raise_on_unexpected_status:
56+
raise errors.UnexpectedStatus(response.status_code, response.content)
57+
else:
58+
return None
59+
60+
61+
def _build_response(
62+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
63+
) -> Response[
64+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
65+
]:
66+
return Response(
67+
status_code=HTTPStatus(response.status_code),
68+
content=response.content,
69+
headers=response.headers,
70+
parsed=_parse_response(client=client, response=response),
71+
)
72+
73+
74+
def sync_detailed(
75+
*,
76+
client: Union[AuthenticatedClient, Client],
77+
) -> Response[
78+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
79+
]:
80+
"""
81+
Raises:
82+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
83+
httpx.TimeoutException: If the request takes longer than Client.timeout.
84+
85+
Returns:
86+
Response[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']]
87+
"""
88+
89+
kwargs = _get_kwargs()
90+
91+
response = client.get_httpx_client().request(
92+
**kwargs,
93+
)
94+
95+
return _build_response(client=client, response=response)
96+
97+
98+
def sync(
99+
*,
100+
client: Union[AuthenticatedClient, Client],
101+
) -> Optional[
102+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
103+
]:
104+
"""
105+
Raises:
106+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
107+
httpx.TimeoutException: If the request takes longer than Client.timeout.
108+
109+
Returns:
110+
Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']
111+
"""
112+
113+
return sync_detailed(
114+
client=client,
115+
).parsed
116+
117+
118+
async def asyncio_detailed(
119+
*,
120+
client: Union[AuthenticatedClient, Client],
121+
) -> Response[
122+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
123+
]:
124+
"""
125+
Raises:
126+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
127+
httpx.TimeoutException: If the request takes longer than Client.timeout.
128+
129+
Returns:
130+
Response[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']]
131+
"""
132+
133+
kwargs = _get_kwargs()
134+
135+
response = await client.get_async_httpx_client().request(**kwargs)
136+
137+
return _build_response(client=client, response=response)
138+
139+
140+
async def asyncio(
141+
*,
142+
client: Union[AuthenticatedClient, Client],
143+
) -> Optional[
144+
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
145+
]:
146+
"""
147+
Raises:
148+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
149+
httpx.TimeoutException: If the request takes longer than Client.timeout.
150+
151+
Returns:
152+
Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']
153+
"""
154+
155+
return (
156+
await asyncio_detailed(
157+
client=client,
158+
)
159+
).parsed

end_to_end_tests/golden-record/my_test_api_client/models/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from .get_location_header_types_int_enum_header import GetLocationHeaderTypesIntEnumHeader
4040
from .get_location_header_types_string_enum_header import GetLocationHeaderTypesStringEnumHeader
4141
from .get_models_allof_response_200 import GetModelsAllofResponse200
42+
from .get_models_oneof_with_required_const_response_200_type_0 import GetModelsOneofWithRequiredConstResponse200Type0
43+
from .get_models_oneof_with_required_const_response_200_type_1 import GetModelsOneofWithRequiredConstResponse200Type1
4244
from .http_validation_error import HTTPValidationError
4345
from .import_ import Import
4446
from .json_like_body import JsonLikeBody
@@ -122,6 +124,8 @@
122124
"GetLocationHeaderTypesIntEnumHeader",
123125
"GetLocationHeaderTypesStringEnumHeader",
124126
"GetModelsAllofResponse200",
127+
"GetModelsOneofWithRequiredConstResponse200Type0",
128+
"GetModelsOneofWithRequiredConstResponse200Type1",
125129
"HTTPValidationError",
126130
"Import",
127131
"JsonLikeBody",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast
2+
3+
from attrs import define as _attrs_define
4+
from attrs import field as _attrs_field
5+
6+
from ..types import UNSET, Unset
7+
8+
T = TypeVar("T", bound="GetModelsOneofWithRequiredConstResponse200Type0")
9+
10+
11+
@_attrs_define
12+
class GetModelsOneofWithRequiredConstResponse200Type0:
13+
"""
14+
Attributes:
15+
type (Literal['alpha']):
16+
color (Union[Unset, str]):
17+
"""
18+
19+
type: Literal["alpha"]
20+
color: Union[Unset, str] = UNSET
21+
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
22+
23+
def to_dict(self) -> Dict[str, Any]:
24+
type = self.type
25+
26+
color = self.color
27+
28+
field_dict: Dict[str, Any] = {}
29+
field_dict.update(self.additional_properties)
30+
field_dict.update(
31+
{
32+
"type": type,
33+
}
34+
)
35+
if color is not UNSET:
36+
field_dict["color"] = color
37+
38+
return field_dict
39+
40+
@classmethod
41+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
42+
d = src_dict.copy()
43+
type = cast(Literal["alpha"], d.pop("type"))
44+
if type != "alpha":
45+
raise ValueError(f"type must match const 'alpha', got '{type}'")
46+
47+
color = d.pop("color", UNSET)
48+
49+
get_models_oneof_with_required_const_response_200_type_0 = cls(
50+
type=type,
51+
color=color,
52+
)
53+
54+
get_models_oneof_with_required_const_response_200_type_0.additional_properties = d
55+
return get_models_oneof_with_required_const_response_200_type_0
56+
57+
@property
58+
def additional_keys(self) -> List[str]:
59+
return list(self.additional_properties.keys())
60+
61+
def __getitem__(self, key: str) -> Any:
62+
return self.additional_properties[key]
63+
64+
def __setitem__(self, key: str, value: Any) -> None:
65+
self.additional_properties[key] = value
66+
67+
def __delitem__(self, key: str) -> None:
68+
del self.additional_properties[key]
69+
70+
def __contains__(self, key: str) -> bool:
71+
return key in self.additional_properties

0 commit comments

Comments
 (0)