1- __all__ = ["Response" , "response_from_data" ]
1+ __all__ = ["HTTPStatusPattern" , " Response" , "response_from_data" ]
22
3- from http import HTTPStatus
43from typing import Optional , TypedDict , Union
54
65from attrs import define
@@ -28,15 +27,85 @@ class _ResponseSource(TypedDict):
2827NONE_SOURCE = _ResponseSource (attribute = "None" , return_type = "None" )
2928
3029
31- @define
30+ class HTTPStatusPattern :
31+ """Status code patterns come in three flavors, in order of precedence:
32+ 1. Specific status codes, such as 200. This is represented by `min` and `max` being the same.
33+ 2. Ranges of status codes, such as 2XX. This is represented by `min` and `max` being different.
34+ 3. The special `default` status code, which is used when no other status codes match. `range` is `None` in this case.
35+
36+ https://github.com/openapi-generators/openapi-python-client/blob/61b6c54994e2a6285bb422ee3b864c45b5d88c15/openapi_python_client/schema/3.1.0.md#responses-object
37+ """
38+
39+ pattern : str
40+ range : Optional [tuple [int , int ]]
41+
42+ def __init__ (self , * , pattern : str , code_range : Optional [tuple [int , int ]]):
43+ """Initialize with a range of status codes or None for the default case."""
44+ self .pattern = pattern
45+ self .range = code_range
46+
47+ @staticmethod
48+ def parse (pattern : str ) -> Union ["HTTPStatusPattern" , ParseError ]:
49+ """Parse a status code pattern such as 2XX or 404"""
50+ if pattern == "default" :
51+ return HTTPStatusPattern (pattern = pattern , code_range = None )
52+
53+ if pattern .endswith ("XX" ) and pattern [0 ].isdigit ():
54+ first_digit = int (pattern [0 ])
55+ return HTTPStatusPattern (pattern = pattern , code_range = (first_digit * 100 , first_digit * 100 + 99 ))
56+
57+ try :
58+ code = int (pattern )
59+ return HTTPStatusPattern (pattern = pattern , code_range = (code , code ))
60+ except ValueError :
61+ return ParseError (
62+ detail = (
63+ f"Invalid response status code pattern: { pattern } , response will be omitted from generated client"
64+ )
65+ )
66+
67+ def is_range (self ) -> bool :
68+ """Check if this is a range of status codes, such as 2XX"""
69+ return self .range is not None and self .range [0 ] != self .range [1 ]
70+
71+ def __lt__ (self , other : "HTTPStatusPattern" ) -> bool :
72+ """Compare two HTTPStatusPattern objects based on the order they should be applied in"""
73+ if self .range is None :
74+ return False # Default gets applied last
75+ if other .range is None :
76+ return True # Other is default, so this one gets applied first
77+
78+ # Specific codes appear before ranges
79+ if self .is_range () and not other .is_range ():
80+ return False
81+ if not self .is_range () and other .is_range ():
82+ return True
83+
84+ # Order specific codes numerically
85+ return self .range [0 ] < other .range [0 ]
86+
87+ def __eq__ (self , other : object ) -> bool :
88+ if not isinstance (other , HTTPStatusPattern ):
89+ return False
90+ return self .range == other .range
91+
92+ def __hash__ (self ) -> int :
93+ return hash (self .range )
94+
95+
96+ @define (order = False )
3297class Response :
3398 """Describes a single response for an endpoint"""
3499
35- status_code : HTTPStatus
100+ status_code : HTTPStatusPattern
36101 prop : Property
37102 source : _ResponseSource
38103 data : Union [oai .Response , oai .Reference ] # Original data which created this response, useful for custom templates
39104
105+ def __lt__ (self , other : "Response" ) -> bool :
106+ """Compare two responses based on the order in which they should be applied in"""
107+ return self .status_code < other .status_code
108+
40109
41110def _source_by_content_type (content_type : str , config : Config ) -> Optional [_ResponseSource ]:
42111 parsed_content_type = utils .get_content_type (content_type , config )
@@ -59,7 +128,7 @@ def _source_by_content_type(content_type: str, config: Config) -> Optional[_Resp
59128
60129def empty_response (
61130 * ,
62- status_code : HTTPStatus ,
131+ status_code : HTTPStatusPattern ,
63132 response_name : str ,
64133 config : Config ,
65134 data : Union [oai .Response , oai .Reference ],
@@ -82,7 +151,7 @@ def empty_response(
82151
83152def response_from_data ( # noqa: PLR0911
84153 * ,
85- status_code : HTTPStatus ,
154+ status_code : HTTPStatusPattern ,
86155 data : Union [oai .Response , oai .Reference ],
87156 schemas : Schemas ,
88157 responses : dict [str , Union [oai .Response , oai .Reference ]],
@@ -91,7 +160,7 @@ def response_from_data( # noqa: PLR0911
91160) -> tuple [Union [Response , ParseError ], Schemas ]:
92161 """Generate a Response from the OpenAPI dictionary representation of it"""
93162
94- response_name = f"response_{ status_code } "
163+ response_name = f"response_{ status_code . pattern } "
95164 if isinstance (data , oai .Reference ):
96165 ref_path = parse_reference_path (data .ref )
97166 if isinstance (ref_path , ParseError ):
0 commit comments