# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import argparse
import optparse
+from telemetry.core import camel_case
-class Command(object):
- @property
- def name(self):
- return self.__class__.__name__.lower()
- @property
- def description(self):
- return self.__doc__
+class ArgumentHandlerMixIn(object):
+ """A structured way to handle command-line arguments.
- def AddCommandLineOptions(self, parser):
- pass
+ In AddCommandLineArgs, add command-line arguments.
+ In ProcessCommandLineArgs, validate them and store them in a private class
+ variable. This way, each class encapsulates its own arguments, without needing
+ to pass an arguments object around everywhere.
+ """
+ @classmethod
+ def AddCommandLineArgs(cls, parser):
+ """Override to accept custom command-line arguments."""
-class ArgparseCommand(Command):
- def ProcessCommandLine(self, parser, args):
- pass
+ @classmethod
+ def ProcessCommandLineArgs(cls, parser, args):
+ """Override to process command-line arguments.
+
+ We pass in parser so we can call parser.error()."""
+
+
+class Command(ArgumentHandlerMixIn):
+ """An abstraction for things that run from the command-line."""
+
+ @classmethod
+ def Name(cls):
+ return camel_case.ToUnderscore(cls.__name__)
+
+ @classmethod
+ def Description(cls):
+ if cls.__doc__:
+ return cls.__doc__.splitlines()[0]
+ else:
+ return ''
def Run(self, args):
raise NotImplementedError()
+ @classmethod
+ def main(cls, args=None):
+ """Main method to run this command as a standalone script."""
+ parser = argparse.ArgumentParser()
+ cls.AddCommandLineArgs(parser)
+ args = parser.parse_args(args=args)
+ cls.ProcessCommandLineArgs(parser, args)
+ return min(cls().Run(args), 255)
+
# TODO: Convert everything to argparse.
class OptparseCommand(Command):
usage = ''
- def CreateParser(self):
- return optparse.OptionParser('%%prog %s %s' % (self.name, self.usage))
-
- def ProcessCommandLine(self, parser, options, args):
- pass
+ @classmethod
+ def CreateParser(cls):
+ return optparse.OptionParser('%%prog %s %s' % (cls.Name(), cls.usage),
+ description=cls.Description())
- def Run(self, options, args):
+ def Run(self, args):
raise NotImplementedError()
+
+ @classmethod
+ def main(cls, args=None):
+ """Main method to run this command as a standalone script."""
+ parser = cls.CreateParser()
+ cls.AddCommandLineArgs(parser)
+ options, args = parser.parse_args(args=args)
+ options.positional_args = args
+ cls.ProcessCommandLineArgs(parser, options)
+ return min(cls().Run(options), 255)
+
+
+class SubcommandCommand(Command):
+ """Combines Commands into one big command with sub-commands.
+
+ E.g. "svn checkout", "svn update", and "svn commit" are separate sub-commands.
+
+ Example usage:
+ class MyCommand(command_line.SubcommandCommand):
+ commands = (Help, List, Run)
+
+ if __name__ == '__main__':
+ sys.exit(MyCommand.main())
+ """
+
+ commands = ()
+
+ @classmethod
+ def AddCommandLineArgs(cls, parser):
+ subparsers = parser.add_subparsers()
+
+ for command in cls.commands:
+ subparser = subparsers.add_parser(
+ command.Name(), help=command.Description())
+ subparser.set_defaults(command=command)
+ command.AddCommandLineArgs(subparser)
+
+ @classmethod
+ def ProcessCommandLineArgs(cls, parser, args):
+ args.command.ProcessCommandLineArgs(parser, args)
+
+ def Run(self, args):
+ return args.command().Run(args)