Coverage for src/pydal2sql/cli.py: 100%
16 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 17:05 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 17:05 +0200
1"""
2CLI tool to generate SQL from PyDAL code.
3"""
5import argparse
6import select
7import string
8import sys
9import textwrap
10from typing import IO, Optional
12import rich
13from rich.prompt import Prompt
15from .helpers import flatten
18class PrettyParser(argparse.ArgumentParser): # pragma: no cover
19 """
20 Add 'rich' to the argparse output.
21 """
23 def _print_message(self, message: str, file: Optional[IO[str]] = None) -> None:
24 rich.print(message, file=file)
27def has_stdin_data() -> bool: # pragma: no cover
28 """
29 Check if the program starts with cli data (pipe | or redirect ><).
31 See Also:
32 https://stackoverflow.com/questions/3762881/how-do-i-check-if-stdin-has-some-data
33 """
34 return any(
35 select.select(
36 [
37 sys.stdin,
38 ],
39 [],
40 [],
41 0.0,
42 )[0]
43 )
46def handle_cli(
47 code: str,
48 db_type: str = None,
49 tables: list[list[str]] = None,
50 verbose: bool = False,
51 noop: bool = False,
52) -> None:
53 """
54 Handle user input.
55 """
56 to_execute = string.Template(
57 textwrap.dedent(
58 """
59 from pydal import *
60 from pydal.objects import *
61 from pydal.validators import *
63 from pydal2sql import generate_sql
65 db = database = DAL(None, migrate=False)
67 tables = $tables
68 db_type = '$db_type'
70 $code
72 if not tables:
73 tables = db._tables
75 for table in tables:
76 print(generate_sql(db[table], db_type))
77 """
78 )
79 )
81 generated_code = to_execute.substitute(
82 {
83 "tables": flatten(tables or []),
84 "db_type": db_type or "",
85 "code": textwrap.dedent(code),
86 }
87 )
88 if verbose or noop:
89 rich.print(generated_code, file=sys.stderr)
91 if not noop:
92 exec(generated_code) # nosec: B102
95def app() -> None: # pragma: no cover
96 """
97 Entrypoint for the pydal2sql cli command.
98 """
99 parser = PrettyParser(
100 prog="pydal2sql",
101 formatter_class=argparse.RawDescriptionHelpFormatter,
102 description="""[green]CLI tool to generate SQL from PyDAL code.[/green]\n
103 Aside from using cli arguments, you can also configure the tool in your code.
104 You can set the following variables:
106 db_type: str = 'sqlite' # your desired database type;
107 tables: list[str] = [] # your desired tables to generate SQL for;""",
108 epilog="Example: [i]cat models.py | pydal2sql sqlite[/i]",
109 )
111 parser.add_argument(
112 "db_type", nargs="?", help="Which database dialect to generate ([blue]postgres, sqlite, mysql[/blue])"
113 )
115 parser.add_argument("--verbose", "-v", help="Show more info", action=argparse.BooleanOptionalAction, default=False)
117 parser.add_argument(
118 "--noop", "-n", help="Only show code, don't run it.", action=argparse.BooleanOptionalAction, default=False
119 )
121 parser.add_argument(
122 "-t",
123 "--table",
124 "--tables",
125 action="append",
126 nargs="+",
127 help="One or more tables to generate. By default, all tables in the file will be used.",
128 )
130 args = parser.parse_args()
132 db_type = args.db_type
134 if not has_stdin_data():
135 if not db_type:
136 db_type = Prompt.ask("Which database type do you want to use?", choices=["sqlite", "postgres", "mysql"])
138 rich.print("Please paste your define tables code below and press ctrl-D when finished.", file=sys.stderr)
140 # else: data from stdin
141 # py code or cli args should define settings.
143 text = sys.stdin.read()
144 return handle_cli(text, db_type, args.table, verbose=args.verbose, noop=args.noop)