Skip to content

Commit 56ab82c

Browse files
committed
Add --no-timings and --no-status
-t/--tuples-only already turns off the timing line and the status footer, but it also drops the headers, the title and the table formatting. Scripts that post-process pgcli output often want the table intact and only one of the two footers gone, and there was no way to ask for that. The two flags are independent of each other and of -t, which keeps working as the psql-compatible shorthand for both. 8 tests, 2 of which fail if the flags are wired to -t instead of standing on their own.
1 parent 924e7d4 commit 56ab82c

3 files changed

Lines changed: 103 additions & 3 deletions

File tree

‎changelog.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Features:
88
* You can specify multiple times.
99
* Runs one statement at a time, like `-f`, and can be combined with `-f`:
1010
both run, the same way psql does.
11+
* Add ``--no-timings`` and ``--no-status`` to suppress the timing line and the
12+
status footer independently. ``-t``/``--tuples-only`` already turns off both,
13+
along with the headers and the title, but there was no way to keep the table
14+
formatting and drop only one of the two, which scripts that post-process the
15+
output often want.
1116
* Add support for forcing destructive commands without confirmation.
1217
* Command line option `-y` or `--yes`.
1318
* Skips the destructive command confirmation prompt when enabled.

‎pgcli/main.py‎

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
OutputSettings = namedtuple(
121121
"OutputSettings",
122122
"table_format dcmlfmt floatfmt column_date_formats missingval expanded max_width case_function style_output "
123-
"max_field_width tuples_only",
123+
"max_field_width tuples_only show_status",
124124
)
125125
OutputSettings.__new__.__defaults__ = (
126126
None,
@@ -134,6 +134,7 @@
134134
None,
135135
DEFAULT_MAX_FIELD_WIDTH,
136136
False,
137+
True,
137138
)
138139

139140

@@ -224,6 +225,8 @@ def __init__(
224225
single_connection=False,
225226
less_chatty=None,
226227
tuples_only=None,
228+
no_timings=False,
229+
no_status=False,
227230
prompt=None,
228231
prompt_dsn=None,
229232
auto_vertical_output=False,
@@ -290,8 +293,14 @@ def __init__(
290293
# alone here and switched to an unadorned one at output time, so \T
291294
# still reports (and can change) the configured format.
292295
self.tuples_only = bool(tuples_only)
293-
if self.tuples_only:
296+
# --no-timings and --no-status turn off one thing each, for scripts that
297+
# want the rows plus one of the two. -t is the psql-compatible shorthand
298+
# that turns off both, along with the headers and the title.
299+
self.show_status = not no_status
300+
if no_timings or self.tuples_only:
294301
self.pgspecial.timing_enabled = False
302+
if self.tuples_only:
303+
self.show_status = False
295304
self.syntax_style = c["main"]["syntax_style"]
296305
self.cli_style = c["colors"]
297306
self.wider_completion_menu = c["main"].as_bool("wider_completion_menu")
@@ -1370,6 +1379,7 @@ def _evaluate_command(self, text):
13701379
style_output=self.style_output,
13711380
max_field_width=self.max_field_width,
13721381
tuples_only=self.tuples_only,
1382+
show_status=self.show_status,
13731383
)
13741384

13751385
# Hide query text for named queries in quiet mode
@@ -1650,6 +1660,20 @@ def echo_via_pager(self, text, color=None):
16501660
default=False,
16511661
help="Print rows only: no column headers, no status footer and no timing, like psql.",
16521662
)
1663+
@click.option(
1664+
"--no-timings",
1665+
"no_timings",
1666+
is_flag=True,
1667+
default=False,
1668+
help="Do not print the timing line after each query.",
1669+
)
1670+
@click.option(
1671+
"--no-status",
1672+
"no_status",
1673+
is_flag=True,
1674+
default=False,
1675+
help="Do not print the status footer (SELECT 3, UPDATE 1, ...) after each query.",
1676+
)
16531677
@click.option("--prompt", help='Prompt format (Default: "\\u@\\h:\\d> ").')
16541678
@click.option(
16551679
"--prompt-dsn",
@@ -1737,6 +1761,8 @@ def cli(
17371761
application_name,
17381762
less_chatty,
17391763
tuples_only,
1764+
no_timings: bool,
1765+
no_status: bool,
17401766
prompt,
17411767
prompt_dsn,
17421768
list_databases,
@@ -1809,6 +1835,8 @@ def cli(
18091835
single_connection=single_connection,
18101836
less_chatty=less_chatty,
18111837
tuples_only=tuples_only,
1838+
no_timings=no_timings,
1839+
no_status=no_status,
18121840
prompt=prompt,
18131841
prompt_dsn=prompt_dsn,
18141842
auto_vertical_output=auto_vertical_output,
@@ -2229,7 +2257,7 @@ def format_status(cur, status):
22292257
output = itertools.chain(output, formatted)
22302258

22312259
# Likewise the status footer.
2232-
if status and not settings.tuples_only:
2260+
if status and settings.show_status and not settings.tuples_only:
22332261
output = itertools.chain(output, [format_status(cur, status)])
22342262

22352263
return output

‎tests/test_no_timings_no_status.py‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from unittest.mock import patch
2+
3+
from click.testing import CliRunner
4+
5+
from pgcli.main import cli, format_output, OutputSettings, PGCli
6+
7+
8+
def test_no_timings_flag_passed_to_pgcli():
9+
runner = CliRunner()
10+
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
11+
runner.invoke(cli, ["--no-timings", "mydb"])
12+
assert mock_pgcli.call_args[1]["no_timings"] is True
13+
14+
15+
def test_no_status_flag_passed_to_pgcli():
16+
runner = CliRunner()
17+
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
18+
runner.invoke(cli, ["--no-status", "mydb"])
19+
assert mock_pgcli.call_args[1]["no_status"] is True
20+
21+
22+
def test_both_default_to_false():
23+
runner = CliRunner()
24+
with patch.object(PGCli, "__init__", autospec=True, return_value=None) as mock_pgcli:
25+
runner.invoke(cli, ["mydb"])
26+
assert mock_pgcli.call_args[1]["no_timings"] is False
27+
assert mock_pgcli.call_args[1]["no_status"] is False
28+
29+
30+
def test_no_timings_turns_off_timing_only():
31+
"""The point of having two flags: each one leaves the other alone."""
32+
cli_obj = PGCli(no_timings=True)
33+
34+
assert cli_obj.pgspecial.timing_enabled is False
35+
assert cli_obj.show_status is True
36+
37+
38+
def test_no_status_turns_off_status_only():
39+
cli_obj = PGCli(no_status=True)
40+
41+
assert cli_obj.show_status is False
42+
assert cli_obj.pgspecial.timing_enabled is True
43+
44+
45+
def test_tuples_only_still_turns_off_both():
46+
"""-t stays the psql-compatible shorthand for both."""
47+
cli_obj = PGCli(tuples_only=True)
48+
49+
assert cli_obj.show_status is False
50+
assert cli_obj.pgspecial.timing_enabled is False
51+
52+
53+
def test_no_status_suppresses_only_the_footer():
54+
settings = OutputSettings(table_format="psql", show_status=False)
55+
output = "\n".join(format_output("Title", [(1,)], ["a"], "SELECT 1", settings))
56+
57+
assert "SELECT 1" not in output
58+
# Rows, headers and title are untouched.
59+
assert "Title" in output
60+
assert "a" in output and "1" in output
61+
62+
63+
def test_status_footer_is_printed_by_default():
64+
settings = OutputSettings(table_format="psql")
65+
output = "\n".join(format_output("Title", [(1,)], ["a"], "SELECT 1", settings))
66+
67+
assert "SELECT 1" in output

0 commit comments

Comments
 (0)