44from typing import Optional , TypedDict , Union
55
66from attrs import define
7+ from typing_extensions import TypeAlias
78
89from openapi_python_client import utils
910from openapi_python_client .parser .properties .schemas import get_reference_simple_name , parse_reference_path
@@ -27,12 +28,48 @@ class _ResponseSource(TypedDict):
2728TEXT_SOURCE = _ResponseSource (attribute = "response.text" , return_type = "str" )
2829NONE_SOURCE = _ResponseSource (attribute = "None" , return_type = "None" )
2930
31+ HTTPStatusSpec : TypeAlias = Union [HTTPStatus , tuple [HTTPStatus , int ]]
32+ """Either a single http status or a tuple representing an inclusive range.
33+
34+ The second element of the tuple is also logically a status code but is typically 299 or similar which
35+ is not contained in the enum.
36+
37+ https://github.com/openapi-generators/openapi-python-client/blob/61b6c54994e2a6285bb422ee3b864c45b5d88c15/openapi_python_client/schema/3.1.0.md#responses-object
38+ """
39+
40+
41+ def http_status_spec (code : str | int ) -> HTTPStatusSpec | ParseError :
42+ """Parses plain integer status codes such as 201 or patterned status codes such as 2XX."""
43+
44+ multiplier = 1
45+ if isinstance (code , str ):
46+ if code .endswith ("XX" ):
47+ code = code .removesuffix ("XX" )
48+ multiplier = 100
49+
50+ try :
51+ status_code = int (code )
52+
53+ if multiplier > 1 :
54+ start = status_code * multiplier
55+ return (HTTPStatus (start ), start + multiplier - 1 )
56+
57+ return HTTPStatus (status_code )
58+ except ValueError :
59+ return ParseError (
60+ detail = (
61+ f"Invalid response status code { code } (not a valid HTTP "
62+ f"status code), response will be omitted from generated "
63+ f"client"
64+ )
65+ )
66+
3067
3168@define
3269class Response :
3370 """Describes a single response for an endpoint"""
3471
35- status_code : HTTPStatus
72+ status_code : HTTPStatusSpec
3673 prop : Property
3774 source : _ResponseSource
3875 data : Union [oai .Response , oai .Reference ] # Original data which created this response, useful for custom templates
@@ -59,7 +96,7 @@ def _source_by_content_type(content_type: str, config: Config) -> Optional[_Resp
5996
6097def empty_response (
6198 * ,
62- status_code : HTTPStatus ,
99+ status_code : HTTPStatusSpec ,
63100 response_name : str ,
64101 config : Config ,
65102 data : Union [oai .Response , oai .Reference ],
@@ -80,18 +117,32 @@ def empty_response(
80117 )
81118
82119
120+ def _status_code_str (status_code_str : str | None , status_code : HTTPStatusSpec ) -> str :
121+ if status_code_str is None :
122+ if isinstance (status_code , HTTPStatus ):
123+ return str (status_code .value )
124+ if isinstance (status_code , int ):
125+ return str (status_code )
126+
127+ raise ValueError (f"status_code_str must be passed for { status_code !r} " )
128+
129+ return status_code_str
130+
131+
83132def response_from_data ( # noqa: PLR0911
84133 * ,
85- status_code : HTTPStatus ,
134+ status_code_str : str | None = None ,
135+ status_code : HTTPStatusSpec ,
86136 data : Union [oai .Response , oai .Reference ],
87137 schemas : Schemas ,
88138 responses : dict [str , Union [oai .Response , oai .Reference ]],
89139 parent_name : str ,
90140 config : Config ,
91141) -> tuple [Union [Response , ParseError ], Schemas ]:
92142 """Generate a Response from the OpenAPI dictionary representation of it"""
143+ status_code_str = _status_code_str (status_code_str , status_code )
93144
94- response_name = f"response_{ status_code } "
145+ response_name = f"response_{ status_code_str } "
95146 if isinstance (data , oai .Reference ):
96147 ref_path = parse_reference_path (data .ref )
97148 if isinstance (ref_path , ParseError ):
0 commit comments