From 93da21026dda47561dd97e4fe5972f74b7c75e54 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 15 Apr 2025 07:23:34 -0700 Subject: [PATCH] Enable cli tab completion --- .config/requirements-test.in | 1 + .pre-commit-config.yaml | 2 + src/ansible_dev_environment/arg_parser.py | 11 +++++ src/ansible_dev_environment/cli.py | 1 + tests/unit/test_cli.py | 53 ++++++++++++++++++++++- 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.config/requirements-test.in b/.config/requirements-test.in index dab3681..989a948 100644 --- a/.config/requirements-test.in +++ b/.config/requirements-test.in @@ -1,3 +1,4 @@ +argcomplete coverage[toml] mypy pip-tools diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2207a19..9e034d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -80,6 +80,7 @@ repos: args: - --output-format=colorized additional_dependencies: + - argcomplete - pytest - pyyaml - subprocess_tee @@ -91,6 +92,7 @@ repos: hooks: - id: mypy additional_dependencies: + - argcomplete - pip - pytest - subprocess_tee diff --git a/src/ansible_dev_environment/arg_parser.py b/src/ansible_dev_environment/arg_parser.py index 97e413c..0ee017d 100644 --- a/src/ansible_dev_environment/arg_parser.py +++ b/src/ansible_dev_environment/arg_parser.py @@ -16,6 +16,14 @@ from .utils import str_to_bool +try: + import argcomplete + + HAS_ARGCOMPLETE = True +except ImportError: # pragma: no cover + HAS_ARGCOMPLETE = False + + logger = logging.getLogger(__name__) if TYPE_CHECKING: @@ -255,6 +263,9 @@ def parse() -> argparse.Namespace: err = "The environment variable 'SKIP_UV' is deprecated, use 'ADE_UV' or '--no-uv' instead." warnings.warn(err, DeprecationWarning, stacklevel=2) + if HAS_ARGCOMPLETE: + argcomplete.autocomplete(parser) + return apply_envvars(args=args, parser=parser) diff --git a/src/ansible_dev_environment/cli.py b/src/ansible_dev_environment/cli.py index 20efa4d..21f5f78 100644 --- a/src/ansible_dev_environment/cli.py +++ b/src/ansible_dev_environment/cli.py @@ -1,3 +1,4 @@ +# PYTHON_ARGCOMPLETE_OK """CLI entrypoint.""" from __future__ import annotations diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index c5247c0..e202535 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -5,9 +5,10 @@ from pathlib import Path from typing import TYPE_CHECKING +import argcomplete import pytest -from ansible_dev_environment.arg_parser import ArgumentParser, apply_envvars +from ansible_dev_environment.arg_parser import ArgumentParser, apply_envvars, parse from ansible_dev_environment.cli import Cli, main @@ -385,3 +386,53 @@ def test_env_wrong_choice( cli.parse_args() captured = capsys.readouterr() assert "choose from 'restrictive', 'cfg', 'none'" in captured.err + + +def test_arg_complete(monkeypatch: pytest.MonkeyPatch) -> None: + """Test argument completion. + + Args: + monkeypatch: Pytest fixture. + """ + inited_parser = None + orig_apply_envvars = apply_envvars + + def _apply_envvars( + args: list[str], + parser: ArgumentParser, + ) -> None: + """Apply environment variables to the argument parser. + + Args: + args: List of arguments. + parser: Argument parser. + """ + nonlocal inited_parser + inited_parser = parser + orig_apply_envvars(args, parser) + + monkeypatch.setattr( + "ansible_dev_environment.arg_parser.apply_envvars", + _apply_envvars, + ) + + monkeypatch.setattr( + "sys.argv", + ["ade", "install"], + ) + parse() + + cli = "ade ins" + monkeypatch.setenv("_ARGCOMPLETE", "1") + monkeypatch.setenv("_ARGCOMPLETE_IFS", "\013") + monkeypatch.setenv("COMP_LINE", cli) + monkeypatch.setenv("COMP_POINT", str(len(cli))) + import io + + str_io = io.StringIO() + + argcomplete.autocomplete(inited_parser, exit_method=print, output_stream=str_io) # type: ignore[arg-type] + + output = str_io.getvalue() + assert "inspect" in output + assert "install" in output