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,77 @@ 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+
88+ @define (order = False )
3289class Response :
3390 """Describes a single response for an endpoint"""
3491
35- status_code : HTTPStatus
92+ status_code : HTTPStatusPattern
3693 prop : Property
3794 source : _ResponseSource
3895 data : Union [oai .Response , oai .Reference ] # Original data which created this response, useful for custom templates
3996
97+ def __lt__ (self , other : "Response" ) -> bool :
98+ """Compare two responses based on the order in which they should be applied in"""
99+ return self .status_code < other .status_code
100+
40101
41102def _source_by_content_type (content_type : str , config : Config ) -> Optional [_ResponseSource ]:
42103 parsed_content_type = utils .get_content_type (content_type , config )
@@ -59,7 +120,7 @@ def _source_by_content_type(content_type: str, config: Config) -> Optional[_Resp
59120
60121def empty_response (
61122 * ,
62- status_code : HTTPStatus ,
123+ status_code : HTTPStatusPattern ,
63124 response_name : str ,
64125 config : Config ,
65126 data : Union [oai .Response , oai .Reference ],
@@ -82,7 +143,7 @@ def empty_response(
82143
83144def response_from_data ( # noqa: PLR0911
84145 * ,
85- status_code : HTTPStatus ,
146+ status_code : HTTPStatusPattern ,
86147 data : Union [oai .Response , oai .Reference ],
87148 schemas : Schemas ,
88149 responses : dict [str , Union [oai .Response , oai .Reference ]],
@@ -91,7 +152,7 @@ def response_from_data( # noqa: PLR0911
91152) -> tuple [Union [Response , ParseError ], Schemas ]:
92153 """Generate a Response from the OpenAPI dictionary representation of it"""
93154
94- response_name = f"response_{ status_code } "
155+ response_name = f"response_{ status_code . pattern } "
95156 if isinstance (data , oai .Reference ):
96157 ref_path = parse_reference_path (data .ref )
97158 if isinstance (ref_path , ParseError ):
0 commit comments