import argparse from jaraco.classes import meta from jaraco import text # type: ignore class Command(metaclass=meta.LeafClassesMeta): """ A general-purpose base class for creating commands for a command-line program using argparse. Each subclass of Command represents a separate sub-command of a program. For example, one might use Command subclasses to implement the Mercurial command set:: class Commit(Command): @staticmethod def add_arguments(cls, parser): parser.add_argument('-m', '--message') @classmethod def run(cls, args): "Run the 'commit' command with args (parsed)" class Merge(Command): pass class Pull(Command): pass ... Then one could create an entry point for Mercurial like so:: def hg_command(): Command.invoke() """ @classmethod def add_subparsers(cls, parser): subparsers = parser.add_subparsers() [cmd_class.add_parser(subparsers) for cmd_class in cls._leaf_classes] @classmethod def add_parser(cls, subparsers): cmd_string = text.words(cls.__name__).lowered().dash_separated() parser = subparsers.add_parser(cmd_string) parser.set_defaults(action=cls) cls.add_arguments(parser) return parser @classmethod def add_arguments(cls, parser): pass @classmethod def invoke(cls): """ Invoke the command using ArgumentParser """ parser = argparse.ArgumentParser() cls.add_subparsers(parser) args = parser.parse_args() args.action.run(args) class Extend(argparse.Action): """ Argparse action to take an nargs=* argument and add any values to the existing value. >>> parser = argparse.ArgumentParser() >>> _ = parser.add_argument('--foo', nargs='*', default=[], action=Extend) >>> args = parser.parse_args(['--foo', 'a=1', '--foo', 'b=2', 'c=3']) >>> args.foo ['a=1', 'b=2', 'c=3'] """ def __call__(self, parser, namespace, values, option_string=None): getattr(namespace, self.dest).extend(values)