@@ -226,14 +226,53 @@ def process_from_openapi(
226226 print (f"Successfully generated { subcommand } in { out_dir } " )
227227
228228
229- def sync_to_openapi (python_path : str , output_path : str ) -> None :
230- """Extract an OpenAPI spec from a Python module."""
229+ def sync_to_openapi (input_path : str , output_path : str ) -> None :
230+ """Extract an OpenAPI spec from a Python module or directory ."""
231231 if not output_path :
232232 output_path = "openapi.json"
233- py_path = Path (python_path )
233+ in_path = Path (input_path )
234234 out_path = Path (output_path )
235235
236- code = py_path .read_text (encoding = "utf-8" )
236+ if in_path .is_dir ():
237+ spec = OpenAPI (
238+ ** {
239+ "openapi" : "3.2.0" ,
240+ "info" : Info (title = "Extracted API" , version = "0.0.1" ),
241+ "paths" : {},
242+ "components" : Components (schemas = {}),
243+ }
244+ ) # type: ignore
245+
246+ client_py = in_path / "client.py"
247+ mock_py = in_path / "mock_server.py"
248+ test_py = in_path / "test_client.py"
249+ cli_py = in_path / "cli_main.py"
250+
251+ if client_py .exists ():
252+ from openapi_client .classes .parse import extract_classes_from_ast
253+ from openapi_client .functions .parse import extract_functions_from_ast
254+
255+ mod = cst .parse_module (client_py .read_text (encoding = "utf-8" ))
256+ extract_classes_from_ast (mod , spec )
257+ extract_functions_from_ast (mod , spec )
258+
259+ if mock_py .exists ():
260+ mod = cst .parse_module (mock_py .read_text (encoding = "utf-8" ))
261+ extract_mocks_from_ast (mod , spec )
262+
263+ if test_py .exists ():
264+ mod = cst .parse_module (test_py .read_text (encoding = "utf-8" ))
265+ extract_tests_from_ast (mod , spec )
266+
267+ if cli_py .exists ():
268+ mod = cst .parse_module (cli_py .read_text (encoding = "utf-8" ))
269+ extract_cli_from_ast (mod , spec )
270+
271+ out_path .write_text (emit_openapi_json (spec , indent = 2 ), encoding = "utf-8" )
272+ print (f"Successfully extracted OpenAPI spec to { out_path } " )
273+ return
274+
275+ code = in_path .read_text (encoding = "utf-8" )
237276
238277 if "argparse" in code and "add_parser" in code :
239278 spec = OpenAPI (
@@ -408,8 +447,26 @@ def main() -> None:
408447 from_openapi_parser = subparsers .add_parser (
409448 "from_openapi" , help = "Generate code from OpenAPI"
410449 )
450+
451+ group = from_openapi_parser .add_mutually_exclusive_group (required = False )
452+ group .add_argument ("-i" , "--input" , type = str , help = "Path to OpenAPI JSON file" )
453+ group .add_argument (
454+ "--input-dir" , type = str , help = "Directory containing OpenAPI specs"
455+ )
456+ from_openapi_parser .add_argument ("-o" , "--output" , type = str , default = "." , help = "Output directory" )
457+ from_openapi_parser .add_argument (
458+ "--no-github-actions" ,
459+ action = "store_true" ,
460+ help = "Do not generate GitHub Actions" ,
461+ )
462+ from_openapi_parser .add_argument (
463+ "--no-installable-package" ,
464+ action = "store_true" ,
465+ help = "Do not generate installable package scaffolding" ,
466+ )
467+
411468 from_openapi_subparsers = from_openapi_parser .add_subparsers (
412- dest = "from_openapi_command"
469+ dest = "from_openapi_command" , required = False
413470 )
414471
415472 for subcmd in ["to_sdk" , "to_sdk_cli" , "to_server" ]:
@@ -435,7 +492,7 @@ def main() -> None:
435492 "to_openapi" , help = "Extract OpenAPI from code"
436493 )
437494 to_openapi_parser .add_argument (
438- "-f " , "--file " , type = str , required = True , help = "Path to Python source file"
495+ "-i " , "--input " , type = str , help = "Path to Python source file or directory" , required = True
439496 )
440497 to_openapi_parser .add_argument (
441498 "-o" ,
@@ -494,19 +551,24 @@ def main() -> None:
494551 if args .command == "sync" :
495552 sync_dir (args .dir )
496553 elif args .command == "from_openapi" :
497- if not args .from_openapi_command :
554+ subcmd = args .from_openapi_command or "to_sdk"
555+ if not args .input and not getattr (args , "input_dir" , None ):
498556 from_openapi_parser .print_help ()
499- sys .exit (0 )
557+ sys .exit (1 )
500558 process_from_openapi (
501- args . from_openapi_command ,
559+ subcmd ,
502560 args .input ,
503- args . input_dir ,
561+ getattr ( args , " input_dir" , None ) ,
504562 args .output ,
505563 args .no_github_actions ,
506564 args .no_installable_package ,
507565 )
508566 elif args .command == "to_openapi" :
509- sync_to_openapi (args .file , args .output )
567+ in_path = args .input
568+ if not in_path :
569+ to_openapi_parser .print_help ()
570+ sys .exit (1 )
571+ sync_to_openapi (in_path , args .output )
510572 elif args .command == "to_docs_json" :
511573 generate_docs_json (args .input , args .no_imports , args .no_wrapping , args .output )
512574 elif args .command == "server_json_rpc" :
0 commit comments