Edit on GitHub

sqlmesh.cli.main

  1from __future__ import annotations
  2
  3import os
  4import sys
  5import typing as t
  6
  7import click
  8
  9from sqlmesh.cli import error_handler
 10from sqlmesh.cli import options as opt
 11from sqlmesh.cli.example_project import ProjectTemplate, init_example_project
 12from sqlmesh.core.context import Context
 13from sqlmesh.utils.date import TimeLike
 14
 15
 16@click.group(no_args_is_help=True)
 17@opt.path
 18@opt.config
 19@click.option(
 20    "--connection",
 21    type=str,
 22    help="The name of the connection.",
 23)
 24@click.option(
 25    "--test-connection",
 26    type=str,
 27    help="The name of the connection to use for tests.",
 28)
 29@click.pass_context
 30@error_handler
 31def cli(
 32    ctx: click.Context,
 33    path: str,
 34    config: t.Optional[str] = None,
 35    connection: t.Optional[str] = None,
 36    test_connection: t.Optional[str] = None,
 37) -> None:
 38    """SQLMesh command line tool."""
 39    if ctx.invoked_subcommand == "version":
 40        return
 41
 42    path = os.path.abspath(path)
 43    if ctx.invoked_subcommand == "init":
 44        ctx.obj = path
 45        return
 46
 47    # Delegates the execution of the --help option to the corresponding subcommand
 48    if "--help" in sys.argv:
 49        return
 50
 51    context = Context(
 52        path=path,
 53        config=config,
 54        connection=connection,
 55        test_connection=test_connection,
 56    )
 57
 58    if not context.models:
 59        raise click.ClickException(
 60            f"`{path}` doesn't seem to have any models... cd into the proper directory or specify the path with --path."
 61        )
 62
 63    ctx.obj = context
 64
 65
 66@cli.command("init")
 67@click.option(
 68    "-t",
 69    "--template",
 70    type=str,
 71    help="Project template. Support values: airflow, default.",
 72)
 73@click.pass_context
 74@error_handler
 75def init(ctx: click.Context, template: t.Optional[str] = None) -> None:
 76    """Create a new SQLMesh repository."""
 77    try:
 78        project_template = ProjectTemplate(template.lower() if template else "default")
 79    except ValueError:
 80        raise click.ClickException(f"Invalid project template '{template}'")
 81    init_example_project(ctx.obj, template=project_template)
 82
 83
 84@cli.command("render")
 85@click.argument("model")
 86@opt.start_time
 87@opt.end_time
 88@opt.latest_time
 89@opt.expand
 90@click.pass_context
 91@error_handler
 92def render(
 93    ctx: click.Context,
 94    model: str,
 95    start: TimeLike,
 96    end: TimeLike,
 97    latest: t.Optional[TimeLike] = None,
 98    expand: t.Optional[t.Union[bool, t.Iterable[str]]] = None,
 99) -> None:
100    """Renders a model's query, optionally expanding referenced models."""
101    snapshot = ctx.obj.snapshots.get(model)
102
103    if not snapshot:
104        raise click.ClickException(f"Model `{model}` not found.")
105
106    rendered = ctx.obj.render(
107        snapshot,
108        start=start,
109        end=end,
110        latest=latest,
111        expand=expand,
112    )
113
114    sql = rendered.sql(pretty=True, dialect=ctx.obj.dialect)
115    ctx.obj.console.show_sql(sql)
116
117
118@cli.command("evaluate")
119@click.argument("model")
120@opt.start_time
121@opt.end_time
122@opt.latest_time
123@click.option(
124    "--limit",
125    type=int,
126    help="The number of rows which the query should be limited to.",
127)
128@click.pass_context
129@error_handler
130def evaluate(
131    ctx: click.Context,
132    model: str,
133    start: TimeLike,
134    end: TimeLike,
135    latest: t.Optional[TimeLike] = None,
136    limit: t.Optional[int] = None,
137) -> None:
138    """Evaluate a model and return a dataframe with a default limit of 1000."""
139    df = ctx.obj.evaluate(
140        model,
141        start=start,
142        end=end,
143        latest=latest,
144        limit=limit,
145    )
146    ctx.obj.console.log_success(df)
147
148
149@cli.command("format")
150@click.pass_context
151@error_handler
152def format(ctx: click.Context) -> None:
153    """Format all models in a given directory."""
154    ctx.obj.format()
155
156
157@cli.command("diff")
158@click.argument("environment")
159@click.pass_context
160@error_handler
161def diff(ctx: click.Context, environment: t.Optional[str] = None) -> None:
162    """Show the diff between the current context and a given environment."""
163    ctx.obj.diff(environment)
164
165
166@cli.command("plan")
167@click.argument("environment", required=False)
168@opt.start_time
169@opt.end_time
170@click.option(
171    "--create-from",
172    type=str,
173    help="The environment to create the target environment from if it doesn't exist. Default: prod.",
174)
175@click.option(
176    "--skip-tests",
177    help="Skip tests prior to generating the plan if they are defined.",
178)
179@click.option(
180    "--restate-model",
181    "-r",
182    type=str,
183    multiple=True,
184    help="Restate data for specified models and models downstream from the one specified. For production environment, all related model versions will have their intervals wiped, but only the current versions will be backfilled. For development environment, only the current model versions will be affected.",
185)
186@click.option(
187    "--no-gaps",
188    is_flag=True,
189    help="Ensure that new snapshots have no data gaps when comparing to existing snapshots for matching models in the target environment.",
190)
191@click.option(
192    "--skip-backfill",
193    is_flag=True,
194    help="Skip the backfill step.",
195)
196@click.option(
197    "--forward-only",
198    is_flag=True,
199    help="Create a plan for forward-only changes.",
200)
201@click.option(
202    "--no-prompts",
203    is_flag=True,
204    help="Disable interactive prompts for the backfill time range. Please note that if this flag is set and there are uncategorized changes, plan creation will fail.",
205)
206@click.option(
207    "--auto-apply",
208    is_flag=True,
209    help="Automatically apply the new plan after creation.",
210)
211@click.option(
212    "--no-auto-categorization",
213    is_flag=True,
214    help="Disable automatic change categorization.",
215    default=None,
216)
217@click.pass_context
218@error_handler
219def plan(ctx: click.Context, environment: t.Optional[str] = None, **kwargs: t.Any) -> None:
220    """Plan a migration of the current context's models with the given environment."""
221    context = ctx.obj
222    restate_models = kwargs.pop("restate_model", None)
223    context.plan(environment, restate_models=restate_models, **kwargs)
224
225
226@cli.command("run")
227@click.argument("environment", required=False)
228@opt.start_time
229@opt.end_time
230@click.option("--skip-janitor", is_flag=True, help="Skip the jantitor task.")
231@click.pass_context
232@error_handler
233def run(ctx: click.Context, environment: t.Optional[str] = None, **kwargs: t.Any) -> None:
234    """Evaluates the DAG of models using the built-in scheduler."""
235    context = ctx.obj
236    context.run(environment, **kwargs)
237
238
239@cli.command("dag")
240@opt.file
241@click.pass_context
242@error_handler
243def dag(ctx: click.Context, file: str) -> None:
244    """
245    Renders the dag using graphviz.
246
247    This command requires a manual install of both the python and system graphviz package.
248    """
249    ctx.obj.render_dag(file)
250
251
252@cli.command("test")
253@opt.match_pattern
254@opt.verbose
255@click.argument("tests", nargs=-1)
256@click.pass_obj
257@error_handler
258def test(obj: Context, k: t.List[str], verbose: bool, tests: t.List[str]) -> None:
259    """Run model unit tests."""
260    # Set Python unittest verbosity level
261    result = obj.test(match_patterns=k, tests=tests, verbose=verbose)
262    if not result.wasSuccessful():
263        exit(1)
264
265
266@cli.command("audit")
267@click.option(
268    "--model",
269    "models",
270    multiple=True,
271    help="A model to audit. Multiple models can be audited.",
272)
273@opt.start_time
274@opt.end_time
275@opt.latest_time
276@click.pass_obj
277@error_handler
278def audit(
279    obj: Context,
280    models: t.Iterator[str],
281    start: TimeLike,
282    end: TimeLike,
283    latest: t.Optional[TimeLike] = None,
284) -> None:
285    """Run audits."""
286    obj.audit(models=models, start=start, end=end, latest=latest)
287
288
289@cli.command("fetchdf")
290@click.argument("sql")
291@click.pass_context
292@error_handler
293def fetchdf(ctx: click.Context, sql: str) -> None:
294    """Runs a sql query and displays the results."""
295    context = ctx.obj
296    context.console.log_success(context.fetchdf(sql))
297
298
299@cli.command("version")
300@error_handler
301def version() -> None:
302    """Print version."""
303    try:
304        from sqlmesh import __version__
305
306        print(__version__)
307    except ImportError:
308        print("Version is not available")
309
310
311if __name__ == "__main__":
312    cli()
cli = <Group cli>

SQLMesh command line tool.

init = <Command init>

Create a new SQLMesh repository.

render = <Command render>

Renders a model's query, optionally expanding referenced models.

evaluate = <Command evaluate>

Evaluate a model and return a dataframe with a default limit of 1000.

format = <Command format>

Format all models in a given directory.

diff = <Command diff>

Show the diff between the current context and a given environment.

plan = <Command plan>

Plan a migration of the current context's models with the given environment.

run = <Command run>

Evaluates the DAG of models using the built-in scheduler.

dag = <Command dag>

Renders the dag using graphviz.

This command requires a manual install of both the python and system graphviz package.

test = <Command test>

Run model unit tests.

audit = <Command audit>

Run audits.

fetchdf = <Command fetchdf>

Runs a sql query and displays the results.

version = <Command version>

Print version.