15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)import argparse 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import optparse 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)from telemetry.core import camel_case 923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class ArgumentHandlerMixIn(object): 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """A structured way to handle command-line arguments. 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) In AddCommandLineArgs, add command-line arguments. 15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) In ProcessCommandLineArgs, validate them and store them in a private class 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) variable. This way, each class encapsulates its own arguments, without needing 17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) to pass an arguments object around everywhere. 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @classmethod 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def AddCommandLineArgs(cls, parser): 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Override to accept custom command-line arguments.""" 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @classmethod 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def ProcessCommandLineArgs(cls, parser, args): 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Override to process command-line arguments. 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) We pass in parser so we can call parser.error().""" 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class Command(ArgumentHandlerMixIn): 320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """An abstraction for things that run from the command-line.""" 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @classmethod 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def Name(cls): 3623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) return camel_case.ToUnderscore(cls.__name__) 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @classmethod 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def Description(cls): 40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if cls.__doc__: 41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return cls.__doc__.splitlines()[0] 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) else: 43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return '' 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def Run(self, args): 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) raise NotImplementedError() 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) @classmethod 49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def main(cls, args=None): 500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Main method to run this command as a standalone script.""" 5123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) parser = argparse.ArgumentParser() 5223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) cls.AddCommandLineArgs(parser) 53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch args = parser.parse_args(args=args) 5423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) cls.ProcessCommandLineArgs(parser, args) 55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return min(cls().Run(args), 255) 5623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# TODO: Convert everything to argparse. 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class OptparseCommand(Command): 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) usage = '' 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) @classmethod 63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def CreateParser(cls): 64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return optparse.OptionParser('%%prog %s %s' % (cls.Name(), cls.usage), 65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) description=cls.Description()) 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def Run(self, args): 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) raise NotImplementedError() 6923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 7023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) @classmethod 71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def main(cls, args=None): 720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Main method to run this command as a standalone script.""" 730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch parser = cls.CreateParser() 7423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) cls.AddCommandLineArgs(parser) 75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch options, args = parser.parse_args(args=args) 7623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) options.positional_args = args 7723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) cls.ProcessCommandLineArgs(parser, options) 78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return min(cls().Run(options), 255) 790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochclass SubcommandCommand(Command): 820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Combines Commands into one big command with sub-commands. 830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch E.g. "svn checkout", "svn update", and "svn commit" are separate sub-commands. 850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch Example usage: 870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch class MyCommand(command_line.SubcommandCommand): 880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch commands = (Help, List, Run) 890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if __name__ == '__main__': 910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch sys.exit(MyCommand.main()) 920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch commands = () 950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch @classmethod 970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def AddCommandLineArgs(cls, parser): 980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch subparsers = parser.add_subparsers() 990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for command in cls.commands: 1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch subparser = subparsers.add_parser( 1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch command.Name(), help=command.Description()) 1030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch subparser.set_defaults(command=command) 1040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch command.AddCommandLineArgs(subparser) 1050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch @classmethod 1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def ProcessCommandLineArgs(cls, parser, args): 1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch args.command.ProcessCommandLineArgs(parser, args) 1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def Run(self, args): 111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return args.command().Run(args) 112