Package logilab :: Package common :: Module clcommands
[frames] | no frames]

Source Code for Module logilab.common.clcommands

  1  # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  2  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
  3  # 
  4  # This file is part of logilab-common. 
  5  # 
  6  # logilab-common is free software: you can redistribute it and/or modify it under 
  7  # the terms of the GNU Lesser General Public License as published by the Free 
  8  # Software Foundation, either version 2.1 of the License, or (at your option) any 
  9  # later version. 
 10  # 
 11  # logilab-common is distributed in the hope that it will be useful, but WITHOUT 
 12  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 13  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 14  # details. 
 15  # 
 16  # You should have received a copy of the GNU Lesser General Public License along 
 17  # with logilab-common.  If not, see <http://www.gnu.org/licenses/>. 
 18  """Helper functions to support command line tools providing more than 
 19  one command. 
 20   
 21  e.g called as "tool command [options] args..." where <options> and <args> are 
 22  command'specific 
 23   
 24   
 25   
 26   
 27  """ 
 28  __docformat__ = "restructuredtext en" 
 29   
 30  # XXX : merge with optparser ? 
 31  import sys 
 32  from os.path import basename 
 33   
 34  from logilab.common.configuration import Configuration 
 35   
 36   
 37  DEFAULT_COPYRIGHT = '''\ 
 38  Copyright (c) 2004-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
 39  http://www.logilab.fr/ -- mailto:contact@logilab.fr''' 
 40   
 41   
 42  DEFAULT_DOC = '''\ 
 43  Type "%prog <command> --help" for more information about a specific 
 44  command. Available commands are :\n''' 
 45   
46 -class BadCommandUsage(Exception):
47 """Raised when an unknown command is used or when a command is not 48 correctly used. 49 """
50 51
52 -class Command(Configuration):
53 """Base class for command line commands.""" 54 arguments = '' 55 name = '' 56 # hidden from help ? 57 hidden = False 58 # max/min args, None meaning unspecified 59 min_args = None 60 max_args = None
61 - def __init__(self, __doc__=None, version=None):
62 if __doc__: 63 usage = __doc__ % (self.name, self.arguments, 64 self.__doc__.replace(' ', '')) 65 else: 66 usage = self.__doc__.replace(' ', '') 67 Configuration.__init__(self, usage=usage, version=version)
68
69 - def check_args(self, args):
70 """check command's arguments are provided""" 71 if self.min_args is not None and len(args) < self.min_args: 72 raise BadCommandUsage('missing argument') 73 if self.max_args is not None and len(args) > self.max_args: 74 raise BadCommandUsage('too many arguments')
75
76 - def run(self, args):
77 """run the command with its specific arguments""" 78 raise NotImplementedError()
79 80
81 -def pop_arg(args_list, expected_size_after=0, msg="Missing argument"):
82 """helper function to get and check command line arguments""" 83 try: 84 value = args_list.pop(0) 85 except IndexError: 86 raise BadCommandUsage(msg) 87 if expected_size_after is not None and len(args_list) > expected_size_after: 88 raise BadCommandUsage('too many arguments') 89 return value
90 91 92 _COMMANDS = {} 93
94 -def register_commands(commands):
95 """register existing commands""" 96 for command_klass in commands: 97 _COMMANDS[command_klass.name] = command_klass
98 99
100 -def main_usage(status=0, doc=DEFAULT_DOC, copyright=DEFAULT_COPYRIGHT):
101 """display usage for the main program (i.e. when no command supplied) 102 and exit 103 """ 104 commands = _COMMANDS.keys() 105 commands.sort() 106 if doc != DEFAULT_DOC: 107 try: 108 doc = doc % ('<command>', '<command arguments>', 109 '''\ 110 Type "%prog <command> --help" for more information about a specific 111 command. Available commands are :\n''') 112 except TypeError: 113 print 'could not find the "command", "arguments" and "default" slots' 114 doc = doc.replace('%prog', basename(sys.argv[0])) 115 print 'usage:', doc 116 max_len = max([len(cmd) for cmd in commands]) # list comprehension for py 2.3 support 117 padding = ' '*max_len 118 for command in commands: 119 cmd = _COMMANDS[command] 120 if not cmd.hidden: 121 title = cmd.__doc__.split('.')[0] 122 print ' ', (command+padding)[:max_len], title 123 if copyright: 124 print '\n', copyright 125 sys.exit(status)
126 127
128 -def cmd_run(cmdname, *args):
129 try: 130 command = _COMMANDS[cmdname](__doc__='%%prog %s %s\n\n%s') 131 except KeyError: 132 raise BadCommandUsage('no %s command' % cmdname) 133 args = command.load_command_line_configuration(args) 134 command.check_args(args) 135 try: 136 command.run(args) 137 except KeyboardInterrupt: 138 print 'interrupted' 139 except BadCommandUsage, err: 140 print 'ERROR: ', err 141 print command.help()
142 143
144 -def main_run(args, doc=DEFAULT_DOC, copyright=DEFAULT_COPYRIGHT):
145 """command line tool""" 146 try: 147 arg = args.pop(0) 148 except IndexError: 149 main_usage(status=1, doc=doc, copyright=copyright) 150 if arg in ('-h', '--help'): 151 main_usage(doc=doc, copyright=copyright) 152 try: 153 cmd_run(arg, *args) 154 except BadCommandUsage, err: 155 print 'ERROR: ', err 156 main_usage(1, doc=doc, copyright=copyright)
157 158
159 -class ListCommandsCommand(Command):
160 """list available commands, useful for bash completion.""" 161 name = 'listcommands' 162 arguments = '[command]' 163 hidden = True 164
165 - def run(self, args):
166 """run the command with its specific arguments""" 167 if args: 168 command = pop_arg(args) 169 cmd = _COMMANDS[command] 170 for optname, optdict in cmd.options: 171 print '--help' 172 print '--' + optname 173 else: 174 commands = _COMMANDS.keys() 175 commands.sort() 176 for command in commands: 177 cmd = _COMMANDS[command] 178 if not cmd.hidden: 179 print command
180 181 register_commands([ListCommandsCommand]) 182