diff --git a/PQAnalysis/cli/main.py b/PQAnalysis/cli/main.py index d5b85f67..4d9dd40c 100644 --- a/PQAnalysis/cli/main.py +++ b/PQAnalysis/cli/main.py @@ -103,20 +103,46 @@ +_ROOT_OPTIONS = ( + "--help", + "--version", + "--progress", + "--logging-level", + "--log-file", +) + + + +def _match_root_option(option: str) -> str | None: + """Resolve a possibly abbreviated long root option like argparse does.""" + matches = [ + root_option for root_option in _ROOT_OPTIONS + if root_option.startswith(option) + ] + return matches[0] if len(matches) == 1 else None + + + def _detect_command(arguments: list[str]) -> str | None: """Scan root options to find the first positional CLI command.""" index = 0 while index < len(arguments): argument = arguments[index] - if argument in {"-h", "--help", "--version"}: + option = argument.partition("=")[0] + matched = ( + _match_root_option(option) + if option.startswith("--") else None + ) + + if argument == "-h" or matched in {"--help", "--version"}: return None - if argument == "--logging-level": + if matched == "--logging-level" and option == argument: index += 2 continue - if argument == "--log-file": + if matched == "--log-file" and option == argument: index += 1 if ( index < len(arguments) and @@ -125,10 +151,6 @@ def _detect_command(arguments: list[str]) -> str | None: index += 1 continue - if argument.startswith(("--logging-level=", "--log-file=")): - index += 1 - continue - if argument.startswith("-"): index += 1 continue diff --git a/tests/cli/test_main.py b/tests/cli/test_main.py index 224b4acd..c96bf864 100644 --- a/tests/cli/test_main.py +++ b/tests/cli/test_main.py @@ -22,9 +22,17 @@ (["--logging-level=INFO", "vacf", "input.in"], "vacf"), (["--log-file", "off", "convert", "rdf.dat"], "convert"), (["--log-file=run.log", "vibrations", "input.in"], "vibrations"), + (["--logging", "DEBUG", "rdf", "input.in"], "rdf"), + (["--logging-lev", "DEBUG", "msd", "input.in"], "msd"), + (["--logging=DEBUG", "vacf", "input.in"], "vacf"), + (["--log-fi", "off", "convert", "rdf.dat"], "convert"), + (["--log-fi=run.log", "vibrations", "input.in"], "vibrations"), + (["--pro", "check_momentum", "traj.vel"], "check_momentum"), (["--help"], None), (["--help", "rdf"], None), + (["--he", "rdf"], None), (["--version", "msd"], None), + (["--vers", "msd"], None), ([], None), ], ) @@ -33,6 +41,42 @@ def test_detect_command(arguments, expected): +def test_main_dispatches_with_abbreviated_root_option(monkeypatch): + main_module = import_module("PQAnalysis.cli.main") + received = {} + + class _FakeCLI: + + @classmethod + def add_arguments(cls, parser): + parser.add_argument("input_file") + + @classmethod + def run(cls, args): + received["input_file"] = args.input_file + + monkeypatch.setattr( + main_module, "_load_command", lambda command: _FakeCLI + ) + monkeypatch.setattr( + sys, + "argv", + ["pqanalysis", "--logging", "DEBUG", "--log-fi", "off", "rdf", "in"], + ) + monkeypatch.setattr(argument_parser, "print_header", lambda: None) + + root_logger = argument_parser.logging.getLogger() + original_level = root_logger.level + + try: + main_module.main() + finally: + root_logger.setLevel(original_level) + + assert received == {"input_file": "in"} + + + @pytest.mark.parametrize( ("command", "module_name", "class_name"), [