Skip to content

Commit

Permalink
fix: import cast for required const properties, since it's used in th…
Browse files Browse the repository at this point in the history
…e 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
  • Loading branch information
benweint authored Oct 28, 2024
1 parent 4fbafa5 commit 40d63f9
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 2 deletions.
41 changes: 41 additions & 0 deletions end_to_end_tests/baseline_openapi_3.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,47 @@
}
}
}
},
"/models/oneof-with-required-const": {
"get": {
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"const": "alpha"
},
"color": {
"type": "string"
}
},
"required": ["type"]
},
{
"type": "object",
"properties": {
"type": {
"const": "beta"
},
"texture": {
"type": "string"
}
},
"required": ["type"]
}
]
}
}
}
}
}
}
}
},
"components": {
Expand Down
41 changes: 41 additions & 0 deletions end_to_end_tests/baseline_openapi_3.1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,47 @@ info:
}
}
},
"/models/oneof-with-required-const": {
"get": {
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"const": "alpha"
},
"color": {
"type": "string"
}
},
"required": ["type"]
},
{
"type": "object",
"properties": {
"type": {
"const": "beta"
},
"texture": {
"type": "string"
}
},
"required": ["type"]
}
]
}
}
}
}
}
}
}
}
"components":
"schemas": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

import types

from . import get_common_parameters, get_models_allof, post_common_parameters, reserved_parameters
from . import (
get_common_parameters,
get_models_allof,
get_models_oneof_with_required_const,
post_common_parameters,
reserved_parameters,
)


class DefaultEndpoints:
Expand All @@ -21,3 +27,7 @@ def reserved_parameters(cls) -> types.ModuleType:
@classmethod
def get_models_allof(cls) -> types.ModuleType:
return get_models_allof

@classmethod
def get_models_oneof_with_required_const(cls) -> types.ModuleType:
return get_models_oneof_with_required_const
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from http import HTTPStatus
from typing import Any, Dict, Optional, Union

import httpx

from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.get_models_oneof_with_required_const_response_200_type_0 import (
GetModelsOneofWithRequiredConstResponse200Type0,
)
from ...models.get_models_oneof_with_required_const_response_200_type_1 import (
GetModelsOneofWithRequiredConstResponse200Type1,
)
from ...types import Response


def _get_kwargs() -> Dict[str, Any]:
_kwargs: Dict[str, Any] = {
"method": "get",
"url": "/models/oneof-with-required-const",
}

return _kwargs


def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
if response.status_code == 200:

def _parse_response_200(
data: object,
) -> Union[
"GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"
]:
try:
if not isinstance(data, dict):
raise TypeError()
response_200_type_0 = GetModelsOneofWithRequiredConstResponse200Type0.from_dict(data)

return response_200_type_0
except: # noqa: E722
pass
if not isinstance(data, dict):
raise TypeError()
response_200_type_1 = GetModelsOneofWithRequiredConstResponse200Type1.from_dict(data)

return response_200_type_1

response_200 = _parse_response_200(response.json())

return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None


def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)


def sync_detailed(
*,
client: Union[AuthenticatedClient, Client],
) -> Response[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
"""
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']]
"""

kwargs = _get_kwargs()

response = client.get_httpx_client().request(
**kwargs,
)

return _build_response(client=client, response=response)


def sync(
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
"""
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']
"""

return sync_detailed(
client=client,
).parsed


async def asyncio_detailed(
*,
client: Union[AuthenticatedClient, Client],
) -> Response[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
"""
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']]
"""

kwargs = _get_kwargs()

response = await client.get_async_httpx_client().request(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[
Union["GetModelsOneofWithRequiredConstResponse200Type0", "GetModelsOneofWithRequiredConstResponse200Type1"]
]:
"""
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union['GetModelsOneofWithRequiredConstResponse200Type0', 'GetModelsOneofWithRequiredConstResponse200Type1']
"""

return (
await asyncio_detailed(
client=client,
)
).parsed
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
from .get_location_header_types_int_enum_header import GetLocationHeaderTypesIntEnumHeader
from .get_location_header_types_string_enum_header import GetLocationHeaderTypesStringEnumHeader
from .get_models_allof_response_200 import GetModelsAllofResponse200
from .get_models_oneof_with_required_const_response_200_type_0 import GetModelsOneofWithRequiredConstResponse200Type0
from .get_models_oneof_with_required_const_response_200_type_1 import GetModelsOneofWithRequiredConstResponse200Type1
from .http_validation_error import HTTPValidationError
from .import_ import Import
from .json_like_body import JsonLikeBody
Expand Down Expand Up @@ -121,6 +123,8 @@
"GetLocationHeaderTypesIntEnumHeader",
"GetLocationHeaderTypesStringEnumHeader",
"GetModelsAllofResponse200",
"GetModelsOneofWithRequiredConstResponse200Type0",
"GetModelsOneofWithRequiredConstResponse200Type1",
"HTTPValidationError",
"Import",
"JsonLikeBody",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from typing import Any, Dict, List, Literal, Type, TypeVar, Union, cast

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

from ..types import UNSET, Unset

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


@_attrs_define
class GetModelsOneofWithRequiredConstResponse200Type0:
"""
Attributes:
type (Literal['alpha']):
color (Union[Unset, str]):
"""

type: Literal["alpha"]
color: Union[Unset, str] = UNSET
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

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

color = self.color

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

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
type = cast(Literal["alpha"], d.pop("type"))
if type != "alpha":
raise ValueError(f"type must match const 'alpha', got '{type}'")

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

get_models_oneof_with_required_const_response_200_type_0 = cls(
type=type,
color=color,
)

get_models_oneof_with_required_const_response_200_type_0.additional_properties = d
return get_models_oneof_with_required_const_response_200_type_0

@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

0 comments on commit 40d63f9

Please sign in to comment.