From 2c9cb8767921bee936d1ec947a61f47ab1759520 Mon Sep 17 00:00:00 2001 From: Anatoly <44327258+aerubanov@users.noreply.github.com> Date: Fri, 5 Jun 2020 23:44:02 +0300 Subject: [PATCH] Subcommand help (#3956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * change DvcParser to print subcommand help * add tests for subcommand help * docstring refactoring * remote: finish separating RemoteTree methods (#3946) * remote: move upload()/download() into tree * remote: move path_info/path_cls into tree * remote.azure: finish moving methods into tree * remote.gs: finish moving methods into tree * remote.hdfs: finish moving methods to tree * remote.http: finish moving methods into tree * remote.oss: finish moving methods into tree * remote.s3: finish moving methods into tree * remote.ssh: finish moving methods into tree * remote.gdrive: finish moving methods into tree * tests: update remote unit tests * tests: update func tests * remote: use walk_files() for all remotes * list_cache_paths() now uses tree.walk_files() for all remotes except local/ssh * fix DS warnings * bugfixes * Unify metrics behavior with dirs (#3935) * run: disable directories for -m/-M flags * run: reformatted string literals * run: simplified metrics check * tests: func: run: dirs as metrics outs * tests: func: run: reformatted string literals * tests: use pytest Co-authored-by: Ruslan Kuprieiev * refactor/test multistage load for params and outputs (#3949) * refactor multistage load for params and outputs * tests: load params * tests: output loading from pipeline file * fix test * fix typo in name * split params load * rename params func s/inject_values/fill_values * fix tests * simplify loads_params and output.load_from_pipeline * address @pared's suggestions for tests * remote: fix build errors (#3957) * remote: fix remaining gdrive build issues (#3959) * tests: fix gdrive build * remote.gdrive: fix walk_files prefix relpath * change DvcParser to print subcommand help * add tests for subcommand help * docstring refactoring * change tests to pytest-style * remove gettext Co-authored-by: Peter Rowlands (변기호) Co-authored-by: nik123 Co-authored-by: Ruslan Kuprieiev Co-authored-by: Saugat Pachhai --- dvc/cli.py | 19 +++++++++++++++++-- tests/func/test_cli.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/dvc/cli.py b/dvc/cli.py index 5ffdc1347..2de78ff60 100644 --- a/dvc/cli.py +++ b/dvc/cli.py @@ -81,18 +81,33 @@ COMMANDS = [ class DvcParser(argparse.ArgumentParser): """Custom parser class for dvc CLI.""" - def error(self, message): + def error(self, message, command=None): """Custom error method. Args: message (str): error message. - + command (str): subcommand name for help message Raises: dvc.exceptions.DvcParser: dvc parser exception. + """ logger.error(message) + if command is not None: + for action in self._actions: + if action.dest == "cmd" and command in action.choices: + subparser = action.choices[command] + subparser.print_help() + raise DvcParserError() self.print_help() raise DvcParserError() + # override this to send subcommand name to error method + def parse_args(self, args=None, namespace=None): + args, argv = self.parse_known_args(args, namespace) + if argv: + msg = "unrecognized arguments: %s" + self.error(msg % " ".join(argv), args.cmd) + return args + class VersionAction(argparse.Action): # pragma: no cover # pylint: disable=too-few-public-methods diff --git a/tests/func/test_cli.py b/tests/func/test_cli.py index bd19836c5..970be5337 100644 --- a/tests/func/test_cli.py +++ b/tests/func/test_cli.py @@ -12,7 +12,7 @@ from dvc.command.remove import CmdRemove from dvc.command.repro import CmdRepro from dvc.command.run import CmdRun from dvc.command.status import CmdDataStatus -from dvc.exceptions import DvcException +from dvc.exceptions import DvcException, DvcParserError from tests.basic_env import TestDvc @@ -171,3 +171,36 @@ class TestFindRoot(TestDvc): args = A() with self.assertRaises(DvcException): CmdBase(args) + + +def test_unknown_command_help(capsys): + try: + _ = parse_args(["unknown"]) + except DvcParserError: + pass + captured = capsys.readouterr() + output = captured.out + try: + _ = parse_args(["--help"]) + except SystemExit: + pass + captured = capsys.readouterr() + help_output = captured.out + assert output == help_output + + +def test_unknown_subcommand_help(capsys): + sample_subcommand = "push" + try: + _ = parse_args([sample_subcommand, "--unknown"]) + except DvcParserError: + pass + captured = capsys.readouterr() + output = captured.out + try: + _ = parse_args([sample_subcommand, "--help"]) + except SystemExit: + pass + captured = capsys.readouterr() + help_output = captured.out + assert output == help_output