1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import argparse 6import optparse 7 8from telemetry.internal.util import camel_case 9 10 11class ArgumentHandlerMixIn(object): 12 """A structured way to handle command-line arguments. 13 14 In AddCommandLineArgs, add command-line arguments. 15 In ProcessCommandLineArgs, validate them and store them in a private class 16 variable. This way, each class encapsulates its own arguments, without needing 17 to pass an arguments object around everywhere. 18 """ 19 20 @classmethod 21 def AddCommandLineArgs(cls, parser): 22 """Override to accept custom command-line arguments.""" 23 24 @classmethod 25 def ProcessCommandLineArgs(cls, parser, args): 26 """Override to process command-line arguments. 27 28 We pass in parser so we can call parser.error().""" 29 30 31class Command(ArgumentHandlerMixIn): 32 """An abstraction for things that run from the command-line.""" 33 34 @classmethod 35 def Name(cls): 36 return camel_case.ToUnderscore(cls.__name__) 37 38 @classmethod 39 def Description(cls): 40 if cls.__doc__: 41 return cls.__doc__.splitlines()[0] 42 else: 43 return '' 44 45 def Run(self, args): 46 raise NotImplementedError() 47 48 @classmethod 49 def main(cls, args=None): 50 """Main method to run this command as a standalone script.""" 51 parser = argparse.ArgumentParser() 52 cls.AddCommandLineArgs(parser) 53 args = parser.parse_args(args=args) 54 cls.ProcessCommandLineArgs(parser, args) 55 return min(cls().Run(args), 255) 56 57 58# TODO: Convert everything to argparse. 59class OptparseCommand(Command): 60 usage = '' 61 62 @classmethod 63 def CreateParser(cls): 64 return optparse.OptionParser('%%prog %s %s' % (cls.Name(), cls.usage), 65 description=cls.Description()) 66 67 @classmethod 68 def AddCommandLineArgs(cls, parser, environment): 69 # pylint: disable=arguments-differ 70 pass 71 72 @classmethod 73 def ProcessCommandLineArgs(cls, parser, args, environment): 74 # pylint: disable=arguments-differ 75 pass 76 77 def Run(self, args): 78 raise NotImplementedError() 79 80 @classmethod 81 def main(cls, args=None): 82 """Main method to run this command as a standalone script.""" 83 parser = cls.CreateParser() 84 cls.AddCommandLineArgs(parser, None) 85 options, args = parser.parse_args(args=args) 86 options.positional_args = args 87 cls.ProcessCommandLineArgs(parser, options, None) 88 return min(cls().Run(options), 255) 89 90 91class SubcommandCommand(Command): 92 """Combines Commands into one big command with sub-commands. 93 94 E.g. "svn checkout", "svn update", and "svn commit" are separate sub-commands. 95 96 Example usage: 97 class MyCommand(command_line.SubcommandCommand): 98 commands = (Help, List, Run) 99 100 if __name__ == '__main__': 101 sys.exit(MyCommand.main()) 102 """ 103 104 commands = () 105 106 @classmethod 107 def AddCommandLineArgs(cls, parser): 108 subparsers = parser.add_subparsers() 109 110 for command in cls.commands: 111 subparser = subparsers.add_parser( 112 command.Name(), help=command.Description()) 113 subparser.set_defaults(command=command) 114 command.AddCommandLineArgs(subparser) 115 116 @classmethod 117 def ProcessCommandLineArgs(cls, parser, args): 118 args.command.ProcessCommandLineArgs(parser, args) 119 120 def Run(self, args): 121 return args.command().Run(args) 122