Package logilab-common-0 :: Package 36 :: Package 1 :: Module clcommands
[frames] | no frames]

Source Code for Module logilab-common-0.36.1.clcommands

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