Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
],
}

_GLOBAL_FLAGS = {"-v", "--verbose"}
_GLOBAL_FLAGS = {"-v", "--verbose", "-q", "--quiet"}
_GLOBAL_OPTIONS = {"--profile", "--format"}


Expand Down Expand Up @@ -83,14 +83,26 @@ def _hoist_global_options(args: list[str]) -> list[str]:
default=False,
help="Show progress details (run IDs, spinners, status messages).",
)
@click.option(
"--quiet",
"-q",
is_flag=True,
default=False,
help="Suppress non-essential output (update notices, progress info).",
)
@click.pass_context
def cli(
ctx: click.Context, profile: str | None, output_format: str, verbose: bool
ctx: click.Context,
profile: str | None,
output_format: str,
verbose: bool,
quiet: bool,
) -> None:
ctx.obj = CliContext(
profile_override=profile,
output_format=OutputFormat(output_format),
verbose=verbose,
quiet=quiet,
)


Expand All @@ -100,7 +112,8 @@ def cli(
def main() -> None:
"""entry point; hoists global options then invokes click."""
sys.argv[1:] = _hoist_global_options(sys.argv[1:])
check_for_updates()
if "-q" not in sys.argv and "--quiet" not in sys.argv:
check_for_updates()
cli()


Expand Down
1 change: 1 addition & 0 deletions cli/types/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ class CliContext:
profile_override: str | None = None
output_format: OutputFormat = field(default=OutputFormat.JSON)
verbose: bool = False
quiet: bool = False
88 changes: 87 additions & 1 deletion tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from __future__ import annotations

from cli.main import _hoist_global_options
from unittest.mock import patch

from click.testing import CliRunner

from cli.main import _hoist_global_options, cli
from cli.types.context import CliContext


class TestHoistGlobalOptions:
Expand Down Expand Up @@ -46,3 +51,84 @@ def test_verbose_long_form(self):
args = ["auth", "list", "--verbose"]
result = _hoist_global_options(args)
assert result[0] == "--verbose"

def test_hoist_quiet_short(self):
result = _hoist_global_options(["realtime", "prices", "-q"])
assert result == ["-q", "realtime", "prices"]

def test_hoist_quiet_long(self):
result = _hoist_global_options(["realtime", "--quiet", "prices"])
assert result == ["--quiet", "realtime", "prices"]

def test_hoist_quiet_and_verbose(self):
result = _hoist_global_options(["realtime", "-q", "-v"])
assert result == ["-q", "-v", "realtime"]

def test_hoist_quiet_with_profile(self):
result = _hoist_global_options(
["explorer", "run", "--quiet", "--profile", "dev"]
)
assert result == ["--quiet", "--profile", "dev", "explorer", "run"]


class TestQuietFlag:
"""--quiet / -q sets quiet=True on CliContext."""

@staticmethod
def _invoke_cli(args: list[str]) -> CliContext:
"""Invoke cli group and return the CliContext stored in ctx.obj."""
import click as _click

captured: dict = {}

@cli.command("_test_noop")
@_click.pass_context
def _noop(ctx: _click.Context) -> None:
captured["obj"] = ctx.obj

try:
runner = CliRunner()
result = runner.invoke(cli, args + ["_test_noop"])
assert result.exit_code == 0, result.output
return captured["obj"]
finally:
cli.commands.pop("_test_noop", None) # type: ignore[union-attr]

def test_quiet_long_flag(self):
ctx = self._invoke_cli(["--quiet"])
assert isinstance(ctx, CliContext)
assert ctx.quiet is True

def test_quiet_short_flag(self):
ctx = self._invoke_cli(["-q"])
assert isinstance(ctx, CliContext)
assert ctx.quiet is True

def test_default_quiet_is_false(self):
ctx = self._invoke_cli([])
assert isinstance(ctx, CliContext)
assert ctx.quiet is False


class TestMainQuietSuppressesUpdateCheck:
"""main() skips check_for_updates when -q/--quiet is in sys.argv."""

@patch("cli.main.check_for_updates")
@patch("cli.main.cli")
def test_quiet_suppresses_update_check(self, mock_cli, mock_check):
with patch("cli.main.sys") as mock_sys:
mock_sys.argv = ["allium", "-q"]
from cli.main import main

main()
mock_check.assert_not_called()

@patch("cli.main.check_for_updates")
@patch("cli.main.cli")
def test_no_quiet_runs_update_check(self, mock_cli, mock_check):
with patch("cli.main.sys") as mock_sys:
mock_sys.argv = ["allium", "realtime"]
from cli.main import main

main()
mock_check.assert_called_once()
Loading