Coverage for src/pydal2sql/cli.py: 100%
56 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-11-13 13:42 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2023-11-13 13:42 +0100
1import sys
2import typing
3from typing import Annotated, Optional
5import typer
6from configuraptor import Singleton
7from pydal2sql_core import get_typing_args
8from pydal2sql_core.cli_support import core_alter, core_create
9from pydal2sql_core.types import DEFAULT_OUTPUT_FORMAT, SUPPORTED_OUTPUT_FORMATS
10from rich import print
11from typer import Argument
12from typing_extensions import Never
14from .__about__ import __version__
15from .typer_support import (
16 DEFAULT_VERBOSITY,
17 IS_DEBUG,
18 ApplicationState,
19 DB_Types,
20 Verbosity,
21 with_exit_code,
22)
24## type fuckery:
25T = typing.TypeVar("T")
27OptionalArgument = Annotated[Optional[T], Argument()]
28# usage: (myparam: OptionalArgument[some_type])
30OptionalOption = Annotated[Optional[T], typer.Option()]
31# usage: (myparam: OptionalOption[some_type])
33DBType_Option = Annotated[DB_Types, typer.Option("--db-type", "--dialect", "-d")]
35Tables_Option = Annotated[
36 Optional[list[str]],
37 typer.Option("--table", "--tables", "-t", help="One or more table names, default is all tables."),
38]
40OutputFormat_Option = Annotated[
41 # Optional[SUPPORTED_OUTPUT_FORMATS],
42 Optional[str],
43 typer.Option("--format", "--fmt", help=f"One of {get_typing_args(SUPPORTED_OUTPUT_FORMATS)}"),
44]
46### end typing stuff, start app:
48app = typer.Typer(
49 no_args_is_help=True,
50)
51state = ApplicationState()
54def info(*args: str) -> None: # pragma: no cover
55 """
56 'print' but with blue text.
57 """
58 print(f"[blue]{' '.join(args)}[/blue]", file=sys.stderr)
61def warn(*args: str) -> None: # pragma: no cover
62 """
63 'print' but with yellow text.
64 """
65 print(f"[yellow]{' '.join(args)}[/yellow]", file=sys.stderr)
68def danger(*args: str) -> None: # pragma: no cover
69 """
70 'print' but with red text.
71 """
72 print(f"[red]{' '.join(args)}[/red]", file=sys.stderr)
75@app.command()
76@with_exit_code(hide_tb=not IS_DEBUG)
77def create(
78 filename: OptionalArgument[str] = None,
79 tables: Tables_Option = None,
80 db_type: DBType_Option = None,
81 magic: Optional[bool] = None,
82 noop: Optional[bool] = None,
83 function: Optional[str] = None,
84 output_format: OutputFormat_Option = DEFAULT_OUTPUT_FORMAT,
85 output_file: Optional[str] = None,
86) -> bool:
87 """
88 todo: docs
90 Examples:
91 pydal2sql create models.py
92 cat models.py | pydal2sql
93 pydal2sql # output from stdin
94 """
95 config = state.update_config(
96 magic=magic, noop=noop, tables=tables, db_type=db_type.value if db_type else None, function=function
97 )
99 if core_create(
100 filename=filename,
101 db_type=config.db_type,
102 tables=config.tables,
103 verbose=state.verbosity > Verbosity.normal,
104 noop=config.noop,
105 magic=config.magic,
106 function=config.function,
107 output_format=typing.cast(SUPPORTED_OUTPUT_FORMATS, output_format),
108 output_file=output_file,
109 ):
110 print("[green] success! [/green]", file=sys.stderr)
111 return True
112 else:
113 print("[red] create failed! [/red]", file=sys.stderr)
114 return False
117@app.command()
118@with_exit_code(hide_tb=not IS_DEBUG)
119def alter(
120 filename_before: OptionalArgument[str] = None,
121 filename_after: OptionalArgument[str] = None,
122 db_type: DBType_Option = None,
123 tables: Tables_Option = None,
124 magic: Optional[bool] = None,
125 noop: Optional[bool] = None,
126 function: Optional[str] = None,
127 output_format: OutputFormat_Option = DEFAULT_OUTPUT_FORMAT,
128 output_file: Optional[str] = None,
129) -> bool:
130 """
131 Todo: docs
133 Examples:
134 > pydal2sql alter @b3f24091a9201d6 examples/magic.py
135 compare magic.py at commit b3f... to current (= as in workdir).
137 > pydal2sql alter examples/magic.py@@b3f24091a9201d6 examples/magic_after_rename.py@latest
138 compare magic.py (which was renamed to magic_after_rename.py),
139 at a specific commit to the latest version in git (ignore workdir version).
141 Todo:
142 alter myfile.py # only one arg
143 # = alter myfile.py@latest myfile.py@current
144 # != alter myfile.py - # with - for cli
145 # != alter - myfile.py
146 """
147 config = state.update_config(
148 magic=magic, noop=noop, tables=tables, db_type=db_type.value if db_type else None, function=function
149 )
151 if core_alter(
152 filename_before,
153 filename_after,
154 db_type=config.db_type,
155 tables=config.tables,
156 verbose=state.verbosity > Verbosity.normal,
157 noop=config.noop,
158 magic=config.magic,
159 function=config.function,
160 output_format=typing.cast(SUPPORTED_OUTPUT_FORMATS, output_format),
161 output_file=output_file,
162 ):
163 print("[green] success! [/green]", file=sys.stderr)
164 return True
165 else:
166 print("[red] alter failed! [/red]", file=sys.stderr)
167 return False
170"""
171todo:
172- db type in config
173- models.py with db import or define_tables method.
174- `public.` prefix
175"""
177"""
178def pin:
179pydal2sql pin 96de5b37b586e75b8ac053b9bef7647f544fe502 # -> default pin created
180pydal2sql alter myfile.py # should compare between pin/@latest and @current
181 # replaces @current for Before, not for After in case of ALTER.
182pydal2sql pin --remove # -> default pin removed
184pydal2sql pin 96de5b37b586e75b8ac053b9bef7647f544fe502 --name my_custom_name # -> pin '@my_custom_name' created
185pydal2sql pin 96de5b37b586e75b8ac053b9bef7647f544fe503 --name my_custom_name #-> pin '@my_custom_name' overwritten
186pydal2sql create myfile.py@my_custom_name
187pydal2sql pin 96de5b37b586e75b8ac053b9bef7647f544fe502 --remove -> pin '@my_custom_name' removed
189pydal2sql pins
190# lists hash with name
191"""
194def show_config_callback() -> Never:
195 """
196 --show-config requested!
197 """
198 print(state)
199 raise typer.Exit(0)
202def version_callback() -> Never:
203 """
204 --version requested!
205 """
206 print(f"pydal2sql Version: {__version__}")
208 raise typer.Exit(0)
211@app.callback(invoke_without_command=True)
212def main(
213 _: typer.Context,
214 config: str = None,
215 verbosity: Verbosity = DEFAULT_VERBOSITY,
216 # stops the program:
217 show_config: bool = False,
218 version: bool = False,
219) -> None:
220 """
221 Todo: docs
223 Args:
224 _: context to determine if a subcommand is passed, etc
225 config: path to a different config toml file
226 verbosity: level of detail to print out (1 - 3)
228 show_config: display current configuration?
229 version: display current version?
231 """
232 if state.config:
233 # if a config already exists, it's outdated, so we clear it.
234 # only really applicable in Pytest scenarios where multiple commands are executed after eachother
235 Singleton.clear(state.config)
237 state.load_config(config_file=config, verbosity=verbosity)
239 if show_config:
240 show_config_callback()
241 elif version:
242 version_callback()
243 # else: just continue
246# if __name__ == "__main__":
247# app()