Skip to content

Commit 9042655

Browse files
authored
Add --quiet option to the query CLI command (#1680)
Resolves #1554
1 parent 24204e7 commit 9042655

File tree

6 files changed

+160
-10
lines changed

6 files changed

+160
-10
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: Features
2+
body: Add `--quiet` option to the `query` CLI command.
3+
time: 2025-02-21T15:35:51.935247-08:00
4+
custom:
5+
Author: plypaul
6+
Issue: "1554"

dbt-metricflow/dbt_metricflow/cli/main.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ def tutorial(ctx: click.core.Context, cfg: CLIConfiguration, message: bool, clea
113113
dbtMetricFlowTutorialHelper.run_tutorial(cfg=cfg, message=message, clean=clean, yes=yes)
114114

115115

116+
def _click_echo(message: str, quiet: bool) -> None:
117+
"""Helper method to call echo depending on whether `quiet` is set."""
118+
if not quiet:
119+
click.echo(message)
120+
121+
116122
@cli.command()
117123
@query_options
118124
@click.option(
@@ -158,6 +164,12 @@ def tutorial(ctx: click.core.Context, cfg: CLIConfiguration, message: bool, clea
158164
required=False,
159165
help="Specify the name of the saved query to use for applicable parameters",
160166
)
167+
@click.option(
168+
"--quiet",
169+
required=False,
170+
help="Minimize output to the console.",
171+
is_flag=True,
172+
)
161173
@pass_config
162174
@exception_handler
163175
@error_if_not_in_dbt_project
@@ -178,12 +190,16 @@ def query(
178190
decimals: int = DEFAULT_RESULT_DECIMAL_PLACES,
179191
show_sql_descriptions: bool = False,
180192
saved_query: Optional[str] = None,
193+
quiet: bool = False,
181194
) -> None:
182195
"""Create a new query with MetricFlow and assembles a MetricFlowQueryResult."""
183196
cfg.setup()
184197
start = time.time()
185-
spinner = Halo(text="Initiating query…", spinner="dots")
186-
spinner.start()
198+
spinner: Optional[Halo] = None
199+
if not quiet:
200+
spinner = Halo(text="Initiating query…", spinner="dots")
201+
spinner.start()
202+
187203
mf_request = MetricFlowQueryRequest.create_with_random_request_id(
188204
saved_query_name=saved_query,
189205
metric_names=metrics,
@@ -203,7 +219,8 @@ def query(
203219
else:
204220
query_result = cfg.mf.query(mf_request=mf_request)
205221

206-
spinner.succeed(f"Success 🦄 - query completed after {time.time() - start:.2f} seconds")
222+
if spinner is not None:
223+
spinner.succeed(f"Success 🦄 - query completed after {time.time() - start:.2f} seconds")
207224

208225
if explain:
209226
assert explain_result
@@ -213,7 +230,7 @@ def query(
213230
else explain_result.sql_statement.sql
214231
)
215232
if show_dataflow_plan:
216-
click.echo("🔎 Generated Dataflow Plan + SQL (remove --explain to see data):")
233+
_click_echo("🔎 Generated Dataflow Plan + SQL (remove --explain to see data):", quiet=quiet)
217234
click.echo(
218235
textwrap.indent(
219236
jinja2.Template(
@@ -230,16 +247,17 @@ def query(
230247
)
231248
click.echo("")
232249
else:
233-
click.echo(
250+
_click_echo(
234251
"🔎 SQL (remove --explain to see data or add --show-dataflow-plan to see the generated dataflow plan):"
235-
"\n"
252+
"\n",
253+
quiet=quiet,
236254
)
237255
click.echo(sql)
238256
if display_plans:
239-
click.echo("Creating temporary directory for storing visualization output.")
257+
_click_echo("Creating temporary directory for storing visualization output.", quiet=quiet)
240258
temp_path = tempfile.mkdtemp()
241259
svg_path = display_dag_as_svg(explain_result.dataflow_plan, temp_path)
242-
click.echo("")
260+
_click_echo("", quiet=quiet)
243261
click.echo(f"Plan SVG saved to: {svg_path}")
244262
exit()
245263

@@ -248,14 +266,14 @@ def query(
248266
# Show the data if returned successfully
249267
if df is not None:
250268
if df.row_count == 0:
251-
click.echo("🕳 Successful MQL query returned an empty result set.")
269+
_click_echo("🕳 Query returned an empty result set", quiet=quiet)
252270
elif csv is not None:
253271
# csv is a LazyFile that is file-like that works in this case.
254272
csv_writer = csv_module.writer(csv)
255273
csv_writer.writerow(df.column_names)
256274
for row in df.rows:
257275
csv_writer.writerow(row)
258-
click.echo(f"🖨 Successfully written query output to {csv.name}")
276+
_click_echo(f"🖨 Wrote query output to {csv.name}", quiet=quiet)
259277
else:
260278
click.echo(df.text_format(decimals))
261279
if display_plans:

tests_metricflow/cli/conftest.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from __future__ import annotations
2+
3+
from typing import Optional, Sequence
4+
5+
import click
6+
from _pytest.capture import CaptureFixture
7+
from _pytest.fixtures import FixtureRequest
8+
from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration
9+
10+
from metricflow.protocols.sql_client import SqlClient
11+
from tests_metricflow.fixtures.cli_fixtures import MetricFlowCliRunner
12+
from tests_metricflow.snapshot_utils import assert_str_snapshot_equal
13+
14+
15+
def run_and_check_cli_command(
16+
request: FixtureRequest,
17+
capsys: CaptureFixture,
18+
mf_test_configuration: MetricFlowTestConfiguration,
19+
cli_runner: MetricFlowCliRunner,
20+
command: click.BaseCommand,
21+
args: Sequence[str],
22+
sql_client: Optional[SqlClient] = None,
23+
assert_zero_exit_code: bool = True,
24+
) -> None:
25+
"""Helper to run a CLI command and check that the output matches the stored snapshot."""
26+
# Needed to resolve `ValueError: I/O operation on closed file` when running CLI tests individually.
27+
# See: https://github.com/pallets/click/issues/824
28+
with capsys.disabled():
29+
result = cli_runner.run(command, args=args)
30+
assert_str_snapshot_equal(
31+
request=request,
32+
mf_test_configuration=mf_test_configuration,
33+
snapshot_id="result",
34+
snapshot_str=result.stdout,
35+
sql_engine=sql_client.sql_engine_type if sql_client else None,
36+
)
37+
if assert_zero_exit_code:
38+
assert result.exit_code == 0
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
5+
import pytest
6+
from _pytest.capture import CaptureFixture
7+
from _pytest.fixtures import FixtureRequest
8+
from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration
9+
10+
from dbt_metricflow.cli.main import query
11+
from metricflow.protocols.sql_client import SqlClient
12+
from tests_metricflow.cli.conftest import run_and_check_cli_command
13+
from tests_metricflow.fixtures.cli_fixtures import MetricFlowCliRunner
14+
15+
logger = logging.getLogger(__name__)
16+
17+
18+
@pytest.mark.sql_engine_snapshot
19+
@pytest.mark.duckdb_only
20+
def test_query(
21+
mf_test_configuration: MetricFlowTestConfiguration,
22+
request: FixtureRequest,
23+
capsys: CaptureFixture,
24+
cli_runner: MetricFlowCliRunner,
25+
sql_client: SqlClient,
26+
) -> None:
27+
"""Test that the `--quiet` flag only shows the table when running a query."""
28+
run_and_check_cli_command(
29+
request=request,
30+
capsys=capsys,
31+
mf_test_configuration=mf_test_configuration,
32+
cli_runner=cli_runner,
33+
command=query,
34+
args=["--metrics", "bookings", "--group-by", "metric_time", "--order", "metric_time", "--quiet"],
35+
sql_client=sql_client,
36+
)
37+
38+
39+
@pytest.mark.sql_engine_snapshot
40+
@pytest.mark.duckdb_only
41+
def test_explain(
42+
mf_test_configuration: MetricFlowTestConfiguration,
43+
request: FixtureRequest,
44+
capsys: CaptureFixture,
45+
cli_runner: MetricFlowCliRunner,
46+
sql_client: SqlClient,
47+
) -> None:
48+
"""Test that the `--quiet` flag only shows the SQL when explaining a query."""
49+
run_and_check_cli_command(
50+
request=request,
51+
capsys=capsys,
52+
mf_test_configuration=mf_test_configuration,
53+
cli_runner=cli_runner,
54+
command=query,
55+
args=["--metrics", "bookings", "--group-by", "metric_time", "--order", "metric_time", "--explain", "--quiet"],
56+
sql_client=sql_client,
57+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
test_name: test_explain
2+
test_filename: test_cli_quiet.py
3+
docstring:
4+
Test that the `--quiet` flag only shows the SQL when explaining a query.
5+
---
6+
SELECT
7+
metric_time__day
8+
, SUM(bookings) AS bookings
9+
FROM (
10+
SELECT
11+
DATE_TRUNC('day', ds) AS metric_time__day
12+
, 1 AS bookings
13+
FROM ***************************.fct_bookings bookings_source_src_10000
14+
) subq_2
15+
GROUP BY
16+
metric_time__day
17+
ORDER BY metric_time__day
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
test_name: test_query
2+
test_filename: test_cli_quiet.py
3+
docstring:
4+
Test that the `--quiet` flag only shows the table when running a query.
5+
---
6+
metric_time__day bookings
7+
------------------- ----------
8+
2019-12-01T00:00:00 1
9+
2019-12-18T00:00:00 10
10+
2019-12-19T00:00:00 18
11+
2019-12-20T00:00:00 2
12+
2020-01-01T00:00:00 5
13+
2020-01-02T00:00:00 9
14+
2020-01-03T00:00:00 1

0 commit comments

Comments
 (0)