-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcli.py
More file actions
130 lines (111 loc) · 3.52 KB
/
Copy pathcli.py
File metadata and controls
130 lines (111 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""CLI interface for json2sql using Typer."""
import sys
from pathlib import Path
import typer
# Lazy imports — converter/dialects pulled on command execution
# to reduce cold start from ~340ms to ~160ms.
try:
from revenueholdings_license import require_license
except ImportError:
import warnings
warnings.warn("revenueholdings-license not installed; license checks skipped", stacklevel=2)
def require_license(product: str) -> None: # type: ignore[misc]
pass
app = typer.Typer(
name="json2sql",
help="Convert JSON files/datasets to SQL INSERT statements.",
no_args_is_help=True,
)
@app.command()
def convert(
input_file: Path | None = typer.Argument( # noqa: B008
None,
help="Path to JSON file. Reads from stdin if not provided.",
exists=True,
),
dialect: str = typer.Option( # noqa: B008
"postgres",
"--dialect",
"-d",
help="SQL dialect: postgres, mysql, sqlite",
),
table: str = typer.Option(
"data",
"--table",
"-t",
help="Table name for INSERT statements.",
),
output: Path | None = typer.Option( # noqa: B008
None,
"--output",
"-o",
help="Output SQL file. Prints to stdout if not provided.",
),
flatten: bool = typer.Option(
False,
"--flatten",
"-f",
help="Flatten nested JSON into relational tables.",
),
schema_only: bool = typer.Option(
False,
"--schema-only",
help="Generate CREATE TABLE statements only (no INSERT).",
),
):
"""Convert a JSON file to SQL INSERT statements."""
from .converter import JSONToSQLConverter
from .dialects import Dialect
# Validate dialect
try:
dialect_enum = Dialect(dialect)
except ValueError:
valid = ", ".join(d.value for d in Dialect)
typer.echo(f"Error: Unknown dialect '{dialect}'. Choose from: {valid}", err=True)
raise typer.Exit(code=1) from None
# Read input
if input_file:
json_text = input_file.read_text(encoding="utf-8")
elif not sys.stdin.isatty():
json_text = sys.stdin.read()
else:
typer.echo("Error: Provide a JSON file or pipe JSON to stdin.", err=True)
raise typer.Exit(code=1)
converter = JSONToSQLConverter(dialect=dialect_enum, flatten=flatten)
try:
if schema_only:
result = converter.generate_schema(json_text, table_name=table)
else:
result = converter.convert(json_text, table_name=table)
except Exception as e:
typer.echo(f"Error converting JSON: {e}", err=True)
raise typer.Exit(code=1) from e
# Write output
if output:
output.write_text(result, encoding="utf-8")
typer.echo(f"SQL written to {output}", err=True)
else:
typer.echo(result)
@app.command()
def mcp() -> None:
"""Run as an MCP (Model Context Protocol) server over stdio.
AI coding agents (Claude Code, Cursor, etc.) use this to interact
with json2sql tools directly.
"""
try:
from click_to_mcp import run # type: ignore[import-untyped]
except ImportError:
typer.echo(
"Error: click_to_mcp is required for MCP mode. "
"Install it with: pip install click-to-mcp",
err=True,
)
raise typer.Exit(code=1) from None
run(app)
@app.command()
def version() -> None:
"""Show version."""
from . import __version__
typer.echo(f"json2sql {__version__}")
if __name__ == "__main__":
app()