command_line.py revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
19f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson# Copyright 2014 The Chromium Authors. All rights reserved.
29f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson# Use of this source code is governed by a BSD-style license that can be
39f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson# found in the LICENSE file.
49f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
59f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport argparse
69f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport optparse
79f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
89f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonfrom telemetry.core import camel_case
99f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonclass ArgumentHandlerMixIn(object):
129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  """A structured way to handle command-line arguments.
139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  In AddCommandLineArgs, add command-line arguments.
159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  In ProcessCommandLineArgs, validate them and store them in a private class
169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  variable. This way, each class encapsulates its own arguments, without needing
179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  to pass an arguments object around everywhere.
189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  """
199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def AddCommandLineArgs(cls, parser):
229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    """Override to accept custom command-line arguments."""
239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def ProcessCommandLineArgs(cls, parser, args):
269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    """Override to process command-line arguments.
279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    We pass in parser so we can call parser.error()."""
299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonclass Command(ArgumentHandlerMixIn):
329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  """Represents a command-line sub-command for use with an argparse sub-parser.
339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  E.g. "svn checkout", "svn update", and "svn commit" are separate sub-commands.
359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  Example usage, to set up argparse to use these commands:
379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    parser = argparse.ArgumentParser()
389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    subparsers = parser.add_subparsers()
399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    for command in COMMANDS:
419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      subparser = subparsers.add_parser(
429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          command.Name(), help=command.Description())
439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      subparser.set_defaults(command=command)
449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      command.AddCommandLineArgs(subparser)
459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    args = parser.parse_args()
479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    args.command.ProcessCommandLineArgs(parser, args)
489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    args.command().Run(args)
499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  """
509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def Name(cls):
539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return camel_case.ToUnderscore(cls.__name__)
549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def Description(cls):
579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if cls.__doc__:
589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return cls.__doc__.splitlines()[0]
599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else:
609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return ''
619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def Run(self, args):
639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    raise NotImplementedError()
649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def main(cls):
679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    parser = argparse.ArgumentParser()
689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls.AddCommandLineArgs(parser)
699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    args = parser.parse_args()
709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls.ProcessCommandLineArgs(parser, args)
719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls().Run(args)
729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson# TODO: Convert everything to argparse.
759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonclass OptparseCommand(Command):
769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  usage = ''
779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def CreateParser(cls):
809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return optparse.OptionParser('%%prog %s %s' % (cls.Name(), cls.usage))
819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def Run(self, args):
839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    raise NotImplementedError()
849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  @classmethod
869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  def main(cls):
879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    parser = optparse.OptionParser()
889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls.AddCommandLineArgs(parser)
899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    options, args = parser.parse_args()
909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    options.positional_args = args
919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls.ProcessCommandLineArgs(parser, options)
929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    cls().Run(options)
939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson