Skip to content

Commit 215f4d2

Browse files
committed
chore: standardize CLI nomenclature, aliases, arguments and programmatic API
This commit consolidates recent changes to standardize CLI docstrings, arguments, secondary flags, operations, and the programmatic SDK API per the consistency plan.
1 parent a48d529 commit 215f4d2

15 files changed

Lines changed: 197 additions & 110 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ cdd-python-all
33
[![License](https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg)](https://opensource.org/licenses/Apache-2.0)
44
[![interactive WASM web demo](https://img.shields.io/badge/interactive-WASM_web_demo-blue.svg)](https://offscale.io/wasm_web_demo)
55
[![CI](https://github.com/offscale/cdd-python-all/actions/workflows/ci.yml/badge.svg)](https://github.com/offscale/cdd-python-all/actions)
6-
[![Test Coverage](https://img.shields.io/badge/test_coverage-100%25-brightgreen.svg)](#)
6+
[![Test Coverage](https://img.shields.io/badge/test_coverage-99%25-brightgreen.svg)](#)
77
[![Doc Coverage](https://img.shields.io/badge/doc_coverage-100%25-brightgreen.svg)](#)
88

99
----

alpine.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ COPY --from=builder /app/dist/*.whl ./
2222
RUN pip install --no-cache-dir ./*.whl
2323

2424
EXPOSE 8080
25-
ENTRYPOINT ["cdd-python", "server_json_rpc", "--listen", "0.0.0.0", "--port", "8080"]
25+
ENTRYPOINT ["cdd-python", "serve_json_rpc", "--listen", "0.0.0.0", "--port", "8080"]

debian.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ COPY --from=builder /app/dist/*.whl ./
2222
RUN pip install --no-cache-dir ./*.whl
2323

2424
EXPOSE 8080
25-
ENTRYPOINT ["cdd-python", "server_json_rpc", "--listen", "0.0.0.0", "--port", "8080"]
25+
ENTRYPOINT ["cdd-python", "serve_json_rpc", "--listen", "0.0.0.0", "--port", "8080"]

src/openapi_client/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,19 @@
55
from openapi_client.routes.emit import ClientGenerator
66
from openapi_client.routes.parse import extract_from_code
77
from openapi_client.models import OpenAPI
8+
from openapi_client.sdk import (
9+
generate_from_openapi,
10+
generate_to_openapi,
11+
generate_docs_json,
12+
serve_json_rpc,
13+
)
814

9-
__all__ = ["ClientGenerator", "extract_from_code", "OpenAPI"]
15+
__all__ = [
16+
"ClientGenerator",
17+
"extract_from_code",
18+
"OpenAPI",
19+
"generate_from_openapi",
20+
"generate_to_openapi",
21+
"generate_docs_json",
22+
"serve_json_rpc",
23+
]

src/openapi_client/cli.py

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ def get_version() -> str:
3030
return "0.0.1"
3131

3232

33-
def apply_env_vars_to_parser(
34-
parser: argparse.ArgumentParser, prefix: str = "CDD_PYTHON_"
35-
):
33+
def apply_env_vars_to_parser(parser: argparse.ArgumentParser, prefix: str = "CDD_"):
3634
"""Recursively set argparse default values from environment variables."""
3735
for action in parser._actions:
3836
if action.dest and action.dest != "help" and action.dest != "==SUPPRESS==":
@@ -208,7 +206,7 @@ def scaffold_github_actions(out_dir: Path, tests: bool = False):
208206
)
209207

210208

211-
def process_from_openapi(
209+
def generate_from_openapi(
212210
subcommand: str,
213211
input_path: str,
214212
input_dir: str,
@@ -333,7 +331,7 @@ def process_from_openapi(
333331
print(f"Successfully generated {subcommand} in {out_dir}")
334332

335333

336-
def sync_to_openapi(input_path: str, output_path: str) -> None:
334+
def generate_to_openapi(input_path: str, output_path: str) -> None:
337335
"""Extract an OpenAPI spec from a Python module or directory."""
338336
if not output_path:
339337
output_path = "openapi.json"
@@ -533,7 +531,7 @@ def sync_dir(project_dir: str) -> None:
533531
print(f"Successfully synced {project_dir}")
534532

535533

536-
def run_json_rpc_server(port: int, listen: str):
534+
def serve_json_rpc(port: int, listen: str):
537535
"""Run a JSON-RPC 2.0 server exposing the CLI subcommands."""
538536
from http.server import BaseHTTPRequestHandler, HTTPServer
539537
import traceback
@@ -565,11 +563,16 @@ def do_POST(self):
565563

566564
try:
567565
if method == "to_openapi":
568-
sync_to_openapi(params.get("file"), params.get("output"))
566+
generate_to_openapi(params.get("input"), params.get("output"))
569567
result = "Success"
570-
elif method == "from_openapi":
571-
process_from_openapi(
572-
params.get("subcommand"),
568+
elif method in (
569+
"from_openapi_to_sdk",
570+
"from_openapi_to_sdk_cli",
571+
"from_openapi_to_server",
572+
):
573+
subcommand = method.replace("from_openapi_", "")
574+
generate_from_openapi(
575+
subcommand,
573576
params.get("input"),
574577
params.get("input_dir"),
575578
params.get("output"),
@@ -628,31 +631,33 @@ def main() -> None:
628631
subparsers = parser.add_subparsers(dest="command")
629632

630633
from_openapi_parser = subparsers.add_parser(
631-
"from_openapi", help="Generate code from OpenAPI"
634+
"from_openapi", help="Generate code from an OpenAPI specification."
632635
)
633636

634637
group = from_openapi_parser.add_mutually_exclusive_group(required=False)
635-
group.add_argument("-i", "--input", type=str, help="Path to OpenAPI JSON file")
636638
group.add_argument(
637-
"--input-dir", type=str, help="Directory containing OpenAPI specs"
639+
"-i", "--input", type=str, help="Path or URL to the OpenAPI specification."
640+
)
641+
group.add_argument(
642+
"--input-dir", type=str, help="Directory containing OpenAPI specifications."
638643
)
639644
from_openapi_parser.add_argument(
640-
"-o", "--output", type=str, default=".", help="Output directory"
645+
"-o", "--output", type=str, default=".", help="Output file or directory path."
641646
)
642647
from_openapi_parser.add_argument(
643648
"--no-github-actions",
644649
action="store_true",
645-
help="Do not generate GitHub Actions",
650+
help="Do not generate GitHub Actions scaffolding.",
646651
)
647652
from_openapi_parser.add_argument(
648653
"--no-installable-package",
649654
action="store_true",
650-
help="Do not generate installable package scaffolding",
655+
help="Do not generate installable package scaffolding.",
651656
)
652657
from_openapi_parser.add_argument(
653658
"--tests",
654659
action="store_true",
655-
help="Create composable tests & mocks",
660+
help="Generate integration tests and mocks.",
656661
)
657662

658663
from_openapi_subparsers = from_openapi_parser.add_subparsers(
@@ -662,43 +667,51 @@ def main() -> None:
662667
for subcmd in ["to_sdk", "to_sdk_cli", "to_server"]:
663668
p = from_openapi_subparsers.add_parser(subcmd)
664669
group = p.add_mutually_exclusive_group(required=False)
665-
group.add_argument("-i", "--input", type=str, help="Path to OpenAPI JSON file")
666670
group.add_argument(
667-
"--input-dir", type=str, help="Directory containing OpenAPI specs"
671+
"-i", "--input", type=str, help="Path or URL to the OpenAPI specification."
672+
)
673+
group.add_argument(
674+
"--input-dir", type=str, help="Directory containing OpenAPI specifications."
675+
)
676+
p.add_argument(
677+
"-o",
678+
"--output",
679+
type=str,
680+
default=".",
681+
help="Output file or directory path.",
668682
)
669-
p.add_argument("-o", "--output", type=str, default=".", help="Output directory")
670683
p.add_argument(
671684
"--no-github-actions",
672685
action="store_true",
673-
help="Do not generate GitHub Actions",
686+
help="Do not generate GitHub Actions scaffolding.",
674687
)
675688
p.add_argument(
676689
"--no-installable-package",
677690
action="store_true",
678-
help="Do not generate installable package scaffolding",
691+
help="Do not generate installable package scaffolding.",
679692
)
680693
p.add_argument(
681694
"--tests",
682695
action="store_true",
683-
help="Create composable tests & mocks",
696+
help="Generate integration tests and mocks.",
684697
)
685698

686699
to_openapi_parser = subparsers.add_parser(
687-
"to_openapi", help="Extract OpenAPI from code"
700+
"to_openapi", help="Generate an OpenAPI specification from source code."
688701
)
689702
to_openapi_parser.add_argument(
690703
"-i",
691704
"--input",
692705
type=str,
693-
help="Path to Python source file or directory",
706+
help="Path to source code directory or file",
694707
required=True,
695708
)
696709
to_openapi_parser.add_argument(
697710
"-o",
698711
"--output",
699712
type=str,
700713
default="openapi.json",
701-
help="Output OpenAPI JSON file",
714+
help="Output file or directory path",
702715
)
703716

704717
sync_parser = subparsers.add_parser(
@@ -713,31 +726,34 @@ def main() -> None:
713726
)
714727

715728
docs_parser = subparsers.add_parser(
716-
"to_docs_json", help="Generate JSON documentation"
729+
"to_docs_json",
730+
help="Generate JSON documentation with code snippets for an OpenAPI specification.",
717731
)
718732
docs_parser.add_argument(
719733
"-i",
720734
"--input",
721735
type=str,
722736
required=True,
723-
help="Path or URL to the OpenAPI specification",
737+
help="Path or URL to the OpenAPI specification.",
724738
)
725739
docs_parser.add_argument(
726-
"--no-imports", action="store_true", help="Omit the imports field"
740+
"--no-imports", action="store_true", help="Omit the imports field."
727741
)
728742
docs_parser.add_argument(
729-
"--no-wrapping", action="store_true", help="Omit the wrapper fields"
743+
"--no-wrapping", action="store_true", help="Omit the wrapper fields."
730744
)
731745
docs_parser.add_argument(
732-
"-o", "--output", type=str, help="Output JSON file (defaults to stdout)"
746+
"-o", "--output", type=str, help="Output file or directory path."
733747
)
734748

735-
server_parser = subparsers.add_parser("server_json_rpc", help="Run JSON-RPC server")
749+
server_parser = subparsers.add_parser(
750+
"serve_json_rpc", help="Expose CLI interface as a JSON-RPC server."
751+
)
736752
server_parser.add_argument(
737-
"--port", type=int, default=8080, help="Port to listen on"
753+
"-p", "--port", type=int, default=8080, help="Port to listen on"
738754
)
739755
server_parser.add_argument(
740-
"--listen", type=str, default="0.0.0.0", help="Address to listen on"
756+
"-l", "--listen", type=str, default="0.0.0.0", help="Address to listen on"
741757
)
742758

743759
apply_env_vars_to_parser(parser)
@@ -754,7 +770,7 @@ def main() -> None:
754770
if not args.input and not getattr(args, "input_dir", None):
755771
from_openapi_parser.print_help()
756772
sys.exit(1)
757-
process_from_openapi(
773+
generate_from_openapi(
758774
subcmd,
759775
args.input,
760776
getattr(args, "input_dir", None),
@@ -765,11 +781,11 @@ def main() -> None:
765781
)
766782
elif args.command == "to_openapi":
767783
in_path = args.input
768-
sync_to_openapi(in_path, args.output)
784+
generate_to_openapi(in_path, args.output)
769785
elif args.command == "to_docs_json":
770786
generate_docs_json(args.input, args.no_imports, args.no_wrapping, args.output)
771-
elif args.command == "server_json_rpc":
772-
run_json_rpc_server(args.port, args.listen)
787+
elif args.command == "serve_json_rpc":
788+
serve_json_rpc(args.port, args.listen)
773789

774790

775791
if __name__ == "__main__": # pragma: no cover

src/openapi_client/functions/utils.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55
import re
66

77

8+
def to_snake_case(name: str) -> str:
9+
"""Convert PascalCase or camelCase to snake_case."""
10+
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
11+
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
12+
13+
814
def sanitize_name(name: str) -> str:
9-
"""Sanitize a string to be a valid Python identifier."""
15+
"""Sanitize a string to be a valid Python identifier in snake_case."""
1016
if not name:
1117
return ""
1218
sanitized = re.sub(r"\W|^(?=\d)", "_", name)
13-
return sanitized
19+
return to_snake_case(sanitized)
1420

1521

1622
def get_annotation_for_schema(s) -> str:

src/openapi_client/sdk.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Programmatic SDK for cdd-python-all."""
2+
3+
from typing import Optional
4+
from openapi_client.cli import generate_from_openapi as cli_generate_from_openapi
5+
from openapi_client.cli import generate_to_openapi as cli_generate_to_openapi
6+
from openapi_client.cli import generate_docs_json as cli_generate_docs_json
7+
from openapi_client.cli import serve_json_rpc as cli_serve_json_rpc
8+
9+
10+
def generate_from_openapi(
11+
input_path: str,
12+
output_dir: str,
13+
no_github_actions: bool = False,
14+
no_installable_package: bool = False,
15+
tests: bool = False,
16+
subcommand: str = "to_sdk",
17+
) -> None:
18+
"""Generate code from an OpenAPI specification."""
19+
cli_generate_from_openapi(
20+
subcommand=subcommand,
21+
input_path=input_path,
22+
output_dir=output_dir,
23+
no_github_actions=no_github_actions,
24+
no_installable_package=no_installable_package,
25+
tests=tests,
26+
)
27+
28+
29+
def generate_to_openapi(input_dir: str, output_path: Optional[str] = None) -> None:
30+
"""Generate an OpenAPI specification from source code."""
31+
cli_generate_to_openapi(input_dir, output_path)
32+
33+
34+
def generate_docs_json(
35+
input_path: str,
36+
no_imports: bool = False,
37+
no_wrapping: bool = False,
38+
output_path: Optional[str] = None,
39+
) -> None:
40+
"""Generate JSON documentation with code snippets for an OpenAPI specification."""
41+
cli_generate_docs_json(input_path, no_imports, no_wrapping, output_path)
42+
43+
44+
def serve_json_rpc(port: int = 8080, listen: str = "127.0.0.1") -> None:
45+
"""Expose CLI interface as a JSON-RPC server."""
46+
cli_serve_json_rpc(port, listen)

0 commit comments

Comments
 (0)