1313 Schema ,
1414 Operation ,
1515 Parameter ,
16- RequestBody ,
1716 Response ,
1817 PathItem ,
1918)
@@ -76,11 +75,6 @@ def is_reference_type(obj: Any) -> bool:
7675
7776
7877def is_schema_type (obj : Any ) -> bool :
79- """Check if object is a Schema type across different versions."""
80- return isinstance (obj , (Schema , Schema30 , Schema31 ))
81-
82-
83- def is_schema_type (obj ) -> bool :
8478 """Check if object is a Schema from any OpenAPI version"""
8579 return isinstance (obj , (Schema30 , Schema31 ))
8680
@@ -130,11 +124,14 @@ def generate_body_param(operation: Operation) -> Union[str, None]:
130124
131125
132126def generate_params (operation : Operation ) -> str :
133- def _generate_params_from_content (content : Union [Reference , Schema ]):
134- if isinstance (content , Reference ):
135- return f"data : { content .ref .split ('/' )[- 1 ]} "
136- else :
137- return f"data : { type_converter (content , True ).converted_type } "
127+ def _generate_params_from_content (content : Any ):
128+ # Accept reference from either 3.0 or 3.1
129+ if isinstance (content , (Reference , Reference30 , Reference31 )):
130+ return f"data : { content .ref .split ('/' )[- 1 ]} " # type: ignore
131+ elif isinstance (content , (Schema , Schema30 , Schema31 )):
132+ return f"data : { type_converter (content , True ).converted_type } " # type: ignore
133+ else : # pragma: no cover
134+ raise Exception (f"Unsupported request body schema type: { type (content )} " )
138135
139136 if operation .parameters is None and operation .requestBody is None :
140137 return ""
@@ -178,42 +175,27 @@ def _generate_params_from_content(content: Union[Reference, Schema]):
178175 "application/octet-stream" ,
179176 ]
180177
181- if operation .requestBody is not None :
182- # Check if this is a RequestBody (either v3.0 or v3.1) by checking for content attribute
183- if (
184- hasattr (operation .requestBody , "content" )
185- and isinstance (operation .requestBody .content , dict )
186- and any (
187- [
188- operation .requestBody .content .get (i ) is not None
189- for i in operation_request_body_types
190- ]
191- )
178+ if operation .requestBody is not None and not is_reference_type (
179+ operation .requestBody
180+ ):
181+ # Safe access only if it's a concrete RequestBody object
182+ rb_content = getattr (operation .requestBody , "content" , None )
183+ if isinstance (rb_content , dict ) and any (
184+ rb_content .get (i ) is not None for i in operation_request_body_types
192185 ):
193- get_keyword = [
194- i
195- for i in operation_request_body_types
196- if operation .requestBody .content .get (i ) is not None
197- ][0 ]
198- content = operation .requestBody .content .get (get_keyword )
199- if content is not None and (
200- hasattr (content , "media_type_schema" )
201- and (
202- hasattr (content .media_type_schema , "type" )
203- or hasattr (content .media_type_schema , "ref" )
204- )
205- ):
206- params += (
207- f"{ _generate_params_from_content (content .media_type_schema )} , "
208- )
209- else :
210- raise Exception (
211- f"Unsupported media type schema for { str (operation )} "
212- ) # pragma: no cover
213- else :
214- raise Exception (
215- f"Unsupported request body type: { type (operation .requestBody )} "
216- )
186+ get_keyword = [i for i in operation_request_body_types if rb_content .get (i )][
187+ 0
188+ ]
189+ content = rb_content .get (get_keyword )
190+ if content is not None and hasattr (content , "media_type_schema" ):
191+ mts = getattr (content , "media_type_schema" , None )
192+ if isinstance (mts , (Reference , Reference30 , Reference31 , Schema , Schema30 , Schema31 )):
193+ params += f"{ _generate_params_from_content (mts )} , "
194+ else : # pragma: no cover
195+ raise Exception (
196+ f"Unsupported media type schema for { str (operation )} : { type (mts )} "
197+ )
198+ # else: silently ignore unsupported body shapes (could extend later)
217199 # Replace - with _ in params
218200 params = params .replace ("-" , "_" )
219201 default_params = default_params .replace ("-" , "_" )
@@ -274,8 +256,8 @@ def generate_return_type(operation: Operation) -> OpReturnType:
274256
275257 if is_response_type (chosen_response ):
276258 # It's a Response type, access content safely
277- if hasattr (chosen_response , "content" ) and chosen_response . content is not None :
278- media_type_schema = chosen_response . content .get ("application/json" )
259+ if hasattr (chosen_response , "content" ) and getattr ( chosen_response , " content" ) is not None : # type: ignore
260+ media_type_schema = getattr ( chosen_response , " content" ) .get ("application/json" ) # type: ignore
279261 elif is_reference_type (chosen_response ):
280262 media_type_schema = create_media_type_for_reference (chosen_response )
281263
@@ -285,40 +267,44 @@ def generate_return_type(operation: Operation) -> OpReturnType:
285267 )
286268
287269 if is_media_type (media_type_schema ):
288- if is_reference_type (media_type_schema .media_type_schema ):
270+ inner_schema = getattr (media_type_schema , "media_type_schema" , None )
271+ if is_reference_type (inner_schema ):
289272 type_conv = TypeConversion (
290- original_type = media_type_schema . media_type_schema . ref ,
291- converted_type = media_type_schema . media_type_schema . ref .split ("/" )[- 1 ],
292- import_types = [media_type_schema . media_type_schema . ref .split ("/" )[- 1 ]],
273+ original_type = inner_schema . ref , # type: ignore
274+ converted_type = inner_schema . ref .split ("/" )[- 1 ], # type: ignore
275+ import_types = [inner_schema . ref .split ("/" )[- 1 ]], # type: ignore
293276 )
294277 return OpReturnType (
295278 type = type_conv ,
296279 status_code = good_responses [0 ][0 ],
297280 complex_type = True ,
298281 )
299- elif is_schema_type (media_type_schema .media_type_schema ):
300- converted_result = type_converter (media_type_schema .media_type_schema , True )
301- if "array" in converted_result .original_type and isinstance (
302- converted_result .import_types , list
282+ elif is_schema_type (inner_schema ):
283+ converted_result = type_converter (inner_schema , True ) # type: ignore
284+ if (
285+ "array" in converted_result .original_type
286+ and isinstance (converted_result .import_types , list )
303287 ):
304288 matched = re .findall (r"List\[(.+)\]" , converted_result .converted_type )
305289 if len (matched ) > 0 :
306290 list_type = matched [0 ]
307- else :
291+ else : # pragma: no cover
308292 raise Exception (
309293 f"Unable to parse list type from { converted_result .converted_type } "
310- ) # pragma: no cover
294+ )
311295 else :
312296 list_type = None
313297 return OpReturnType (
314298 type = converted_result ,
315299 status_code = good_responses [0 ][0 ],
316- complex_type = converted_result .import_types is not None
317- and len (converted_result .import_types ) > 0 ,
300+ complex_type = bool (
301+ converted_result .import_types
302+ and len (converted_result .import_types ) > 0
303+ ),
318304 list_type = list_type ,
319305 )
320- else :
321- raise Exception ("Unknown media type schema type" ) # pragma: no cover
306+ else : # pragma: no cover
307+ raise Exception ("Unknown media type schema type" )
322308 elif media_type_schema is None :
323309 return OpReturnType (
324310 type = None ,
@@ -342,7 +328,40 @@ def generate_services(
342328 def generate_service_operation (
343329 op : Operation , path_name : str , async_type : bool
344330 ) -> ServiceOperation :
331+ # Merge path-level parameters (always required by spec) into the
332+ # operation-level parameters so they get turned into function args.
333+ try :
334+ path_level_params = []
335+ if hasattr (path , "parameters" ) and getattr (path , "parameters" ) is not None : # type: ignore
336+ path_level_params = [p for p in getattr (path , "parameters" ) if p is not None ] # type: ignore
337+ if path_level_params :
338+ existing_names = set ()
339+ if op .parameters is not None :
340+ for p in op .parameters : # type: ignore
341+ if isinstance (p , Parameter ):
342+ existing_names .add (p .name )
343+ for p in path_level_params :
344+ if isinstance (p , Parameter ) and p .name not in existing_names :
345+ if op .parameters is None :
346+ op .parameters = [] # type: ignore
347+ op .parameters .append (p ) # type: ignore
348+ except Exception : # pragma: no cover
349+ pass
350+
345351 params = generate_params (op )
352+ # Fallback: ensure all {placeholders} in path are present as function params
353+ try :
354+ placeholder_names = [m .group (1 ) for m in re .finditer (r"\{([^}/]+)\}" , path_name )]
355+ existing_param_names = {
356+ p .split (":" )[0 ].strip ()
357+ for p in params .split ("," ) if ":" in p
358+ }
359+ for ph in placeholder_names :
360+ norm_ph = common .normalize_symbol (ph )
361+ if norm_ph not in existing_param_names and norm_ph :
362+ params = f"{ norm_ph } : Any, " + params
363+ except Exception : # pragma: no cover
364+ pass
346365 operation_id = generate_operation_id (op , http_operation , path_name )
347366 query_params = generate_query_params (op )
348367 header_params = generate_header_params (op )
@@ -395,6 +414,11 @@ def generate_service_operation(
395414 async_so = generate_service_operation (op , path_name , True )
396415 service_ops .append (async_so )
397416
417+ # Ensure every operation has a tag; fallback to "default" for untagged operations
418+ for so in service_ops :
419+ if not so .tag :
420+ so .tag = "default"
421+
398422 tags = set ([so .tag for so in service_ops ])
399423
400424 for tag in tags :
0 commit comments