1"""Command line interface for dvc.""" 2 3from __future__ import print_function 4from __future__ import unicode_literals 5 6import sys 7import argparse 8 9import dvc.logger as logger 10from dvc.command.base import fix_subparsers 11import dvc.command.init as init 12import dvc.command.destroy as destroy 13import dvc.command.remove as remove 14import dvc.command.move as move 15import dvc.command.unprotect as unprotect 16import dvc.command.run as run 17import dvc.command.repro as repro 18import dvc.command.data_sync as data_sync 19import dvc.command.gc as gc 20import dvc.command.add as add 21import dvc.command.imp as imp 22import dvc.command.config as config 23import dvc.command.checkout as checkout 24import dvc.command.remote as remote 25import dvc.command.cache as cache 26import dvc.command.metrics as metrics 27import dvc.command.install as install 28import dvc.command.root as root 29import dvc.command.lock as lock 30import dvc.command.pipeline as pipeline 31import dvc.command.daemon as daemon 32import dvc.command.commit as commit 33from dvc.exceptions import DvcParserError 34from dvc import VERSION 35 36 37COMMANDS = [ 38 init, 39 destroy, 40 add, 41 remove, 42 move, 43 unprotect, 44 run, 45 repro, 46 data_sync, 47 gc, 48 add, 49 imp, 50 config, 51 checkout, 52 remote, 53 cache, 54 metrics, 55 install, 56 root, 57 lock, 58 pipeline, 59 daemon, 60 commit, 61] 62 63 64class DvcParser(argparse.ArgumentParser): 65 """Custom parser class for dvc CLI.""" 66 67 def error(self, message): 68 """Custom error method. 69 Args: 70 message (str): error message. 71 72 Raises: 73 dvc.exceptions.DvcParser: dvc parser exception. 74 """ 75 logger.error(message) 76 self.print_help() 77 raise DvcParserError() 78 79 80class VersionAction(argparse.Action): # pragma: no cover 81 # pylint: disable=too-few-public-methods 82 """Shows dvc version and exits.""" 83 84 def __call__(self, parser, namespace, values, option_string=None): 85 print(VERSION) 86 sys.exit(0) 87 88 89def get_parent_parser(): 90 """Create instances of a parser containing common arguments shared among 91 all the commands. 92 93 When overwritting `-q` or `-v`, you need to instantiate a new object 94 in order to prevent some weird behavior. 95 """ 96 parent_parser = argparse.ArgumentParser(add_help=False) 97 98 log_level_group = parent_parser.add_mutually_exclusive_group() 99 log_level_group.add_argument( 100 "-q", "--quiet", action="store_true", default=False, help="Be quiet." 101 ) 102 log_level_group.add_argument( 103 "-v", 104 "--verbose", 105 action="store_true", 106 default=False, 107 help="Be verbose.", 108 ) 109 110 return parent_parser 111 112 113def parse_args(argv=None): 114 """Parses CLI arguments. 115 116 Args: 117 argv: optional list of arguments to parse. sys.argv is used by default. 118 119 Raises: 120 dvc.exceptions.DvcParserError: raised for argument parsing errors. 121 """ 122 parent_parser = get_parent_parser() 123 124 # Main parser 125 desc = "Data Version Control" 126 parser = DvcParser( 127 prog="dvc", 128 description=desc, 129 parents=[parent_parser], 130 formatter_class=argparse.RawTextHelpFormatter, 131 ) 132 133 # NOTE: On some python versions action='version' prints to stderr 134 # instead of stdout https://bugs.python.org/issue18920 135 parser.add_argument( 136 "-V", 137 "--version", 138 action=VersionAction, 139 nargs=0, 140 help="Show program's version.", 141 ) 142 143 # Sub commands 144 subparsers = parser.add_subparsers( 145 title="Available Commands", 146 metavar="COMMAND", 147 dest="cmd", 148 help="Use dvc COMMAND --help for command-specific help.", 149 ) 150 151 fix_subparsers(subparsers) 152 153 for cmd in COMMANDS: 154 cmd.add_parser(subparsers, parent_parser) 155 156 args = parser.parse_args(argv) 157 158 return args 159