1- __all__ = ["Response" , "response_from_data" ]
1+ __all__ = ["Response" , "response_from_data" , "HTTPStatusPattern" ]
22
3- from http import HTTPStatus
43from typing import Optional , TypedDict , Union
54
65from attrs import define
@@ -27,16 +26,77 @@ class _ResponseSource(TypedDict):
2726TEXT_SOURCE = _ResponseSource (attribute = "response.text" , return_type = "str" )
2827NONE_SOURCE = _ResponseSource (attribute = "None" , return_type = "None" )
2928
30-
31- @define
29+ class HTTPStatusPattern :
30+ """Status code patterns come in three flavors, in order of precedence:
31+ 1. Specific status codes, such as 200. This is represented by `min` and `max` being the same.
32+ 2. Ranges of status codes, such as 2XX. This is represented by `min` and `max` being different.
33+ 3. The special `default` status code, which is used when no other status codes match. `range` is `None` in this case.
34+
35+ https://github.com/openapi-generators/openapi-python-client/blob/61b6c54994e2a6285bb422ee3b864c45b5d88c15/openapi_python_client/schema/3.1.0.md#responses-object
36+ """
37+
38+ pattern : str
39+ range : Optional [tuple [int , int ]]
40+
41+ def __init__ (self , * , pattern : str , code_range : Optional [tuple [int , int ]]):
42+ """Initialize with a range of status codes or None for the default case."""
43+ self .pattern = pattern
44+ self .range = code_range
45+
46+ @staticmethod
47+ def parse (pattern : str ) -> Union ["HTTPStatusPattern" , ParseError ]:
48+ """Parse a status code pattern such as 2XX or 404"""
49+ if pattern == "default" :
50+ return HTTPStatusPattern (pattern = pattern , code_range = None )
51+
52+ if pattern .endswith ("XX" ) and pattern [0 ].isdigit ():
53+ first_digit = int (pattern [0 ])
54+ return HTTPStatusPattern (pattern = pattern , code_range = (first_digit * 100 , first_digit * 100 + 99 ))
55+
56+ try :
57+ code = int (pattern )
58+ return HTTPStatusPattern (pattern = pattern , code_range = (code , code ))
59+ except ValueError :
60+ return ParseError (
61+ detail = (
62+ f"Invalid response status code pattern: { pattern } , response will be omitted from generated client"
63+ )
64+ )
65+
66+ def is_range (self ) -> bool :
67+ """Check if this is a range of status codes, such as 2XX"""
68+ return self .range is not None and self .range [0 ] != self .range [1 ]
69+
70+ def __lt__ (self , other : "HTTPStatusPattern" ) -> bool :
71+ """Compare two HTTPStatusPattern objects based on the order they should be applied in"""
72+ if self .range is None :
73+ return False # Default gets applied last
74+ if other .range is None :
75+ return True # Other is default, so this one gets applied first
76+
77+ # Specific codes appear before ranges
78+ if self .is_range () and not other .is_range ():
79+ return False
80+ if not self .is_range () and other .is_range ():
81+ return True
82+
83+ # Order specific codes numerically
84+ return self .range [0 ] < other .range [0 ]
85+
86+
87+ @define (order = False )
3288class Response :
3389 """Describes a single response for an endpoint"""
3490
35- status_code : HTTPStatus
91+ status_code : HTTPStatusPattern
3692 prop : Property
3793 source : _ResponseSource
3894 data : Union [oai .Response , oai .Reference ] # Original data which created this response, useful for custom templates
3995
96+ def __lt__ (self , other ) -> bool :
97+ """Compare two responses based on the order in which they should be applied in"""
98+ return self .status_code < other .status_code
99+
40100
41101def _source_by_content_type (content_type : str , config : Config ) -> Optional [_ResponseSource ]:
42102 parsed_content_type = utils .get_content_type (content_type , config )
@@ -59,7 +119,7 @@ def _source_by_content_type(content_type: str, config: Config) -> Optional[_Resp
59119
60120def empty_response (
61121 * ,
62- status_code : HTTPStatus ,
122+ status_code : HTTPStatusPattern ,
63123 response_name : str ,
64124 config : Config ,
65125 data : Union [oai .Response , oai .Reference ],
@@ -82,7 +142,7 @@ def empty_response(
82142
83143def response_from_data ( # noqa: PLR0911
84144 * ,
85- status_code : HTTPStatus ,
145+ status_code : HTTPStatusPattern ,
86146 data : Union [oai .Response , oai .Reference ],
87147 schemas : Schemas ,
88148 responses : dict [str , Union [oai .Response , oai .Reference ]],
@@ -91,7 +151,7 @@ def response_from_data( # noqa: PLR0911
91151) -> tuple [Union [Response , ParseError ], Schemas ]:
92152 """Generate a Response from the OpenAPI dictionary representation of it"""
93153
94- response_name = f"response_{ status_code } "
154+ response_name = f"response_{ status_code . pattern } "
95155 if isinstance (data , oai .Reference ):
96156 ref_path = parse_reference_path (data .ref )
97157 if isinstance (ref_path , ParseError ):
0 commit comments