This is how I like to do it in my command line tool Python scripts.
I have a main class that represents the tool:
class MyToolCommandLineDriver(object):
@classmethod
def subcommand_map(cls):
# create a mapping of subcommand names to classes
return subcommand_map
@classmethod
def run(cls):
subcommand_map = cls.subcommand_map()
parser = argparse.ArgumentParser(description='Some Tool')
parser.add_argument('--some_global_option', help='Some Help')
subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand_name')
for subcommand_name, subcommand_class in subcommand_map.items():
subparser = subparsers.add_parser(subcommand_name, help=subcommand_class.__doc__)
subcommand_class.configure_argument_parser(subparser)
if __name__ == "__main__":
MyToolCommandLineDriver.run()
Each of those subcommand_class
items derives from a common base class and gets a chance to configure its own parser:
class AbstractSubcommand(object):
@classmethod
def configure_argument_parser(cls, parser):
pass
class SubcommandFooBar(AbstractSubcommand):
@classmethod
def configure_argument_parser(cls, parser):
parser.add_argument('some_argument', nargs='+', help='Some help')
You can see the full example here: https://github.com/liyanage/git-tools/blob/master/githelper/githelper.py
That is a subcommand-based tool I made. This setup lets each subcommand define its specific arguments, and it also results in useful usage messages for free, both at the top-level:
$ githelper.py -h
usage: githelper.py [-h] [--root_path ROOT_PATH] [--verbose]
{status,reset-master-to-svn-branch,svn-conflicts,clone-externals,tree,svn-lineage,svn-diff,branch,each,svn-delete-resolve,svn-mergeinfo,svn-rebase,foo,checkout}
and for each subcommand:
$ githelper.py clone-externals -h
usage: githelper.py clone-externals [-h] svn_url checkout_directory
positional arguments:
svn_url The toplevel SVN repository to clone
checkout_directory The path to the sandbox directory to create