| OLD | NEW |
| 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # | 3 # |
| 4 # This file is part of logilab-common. | 4 # This file is part of logilab-common. |
| 5 # | 5 # |
| 6 # logilab-common is free software: you can redistribute it and/or modify it unde
r | 6 # logilab-common is free software: you can redistribute it and/or modify it unde
r |
| 7 # the terms of the GNU Lesser General Public License as published by the Free | 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) an
y | 8 # Software Foundation, either version 2.1 of the License, or (at your option) an
y |
| 9 # later version. | 9 # later version. |
| 10 # | 10 # |
| 11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT | 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 | 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 | 13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| 14 # details. | 14 # details. |
| 15 # | 15 # |
| 16 # You should have received a copy of the GNU Lesser General Public License along | 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/>. | 17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| 18 """Helper functions to support command line tools providing more than | 18 """Helper functions to support command line tools providing more than |
| 19 one command. | 19 one command. |
| 20 | 20 |
| 21 e.g called as "tool command [options] args..." where <options> and <args> are | 21 e.g called as "tool command [options] args..." where <options> and <args> are |
| 22 command'specific | 22 command'specific |
| 23 """ | 23 """ |
| 24 | 24 |
| 25 from __future__ import print_function | |
| 26 | |
| 27 __docformat__ = "restructuredtext en" | 25 __docformat__ = "restructuredtext en" |
| 28 | 26 |
| 29 import sys | 27 import sys |
| 30 import logging | 28 import logging |
| 31 from os.path import basename | 29 from os.path import basename |
| 32 | 30 |
| 33 from logilab.common.configuration import Configuration | 31 from logilab.common.configuration import Configuration |
| 34 from logilab.common.logging_ext import init_log, get_threshold | 32 from logilab.common.logging_ext import init_log, get_threshold |
| 35 from logilab.common.deprecation import deprecated | 33 from logilab.common.deprecation import deprecated |
| 36 | 34 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 init_log(debug=True, # so that we use StreamHandler | 108 init_log(debug=True, # so that we use StreamHandler |
| 111 logthreshold=self.logthreshold, | 109 logthreshold=self.logthreshold, |
| 112 logformat='%(levelname)s: %(message)s') | 110 logformat='%(levelname)s: %(message)s') |
| 113 try: | 111 try: |
| 114 arg = args.pop(0) | 112 arg = args.pop(0) |
| 115 except IndexError: | 113 except IndexError: |
| 116 self.usage_and_exit(1) | 114 self.usage_and_exit(1) |
| 117 if arg in ('-h', '--help'): | 115 if arg in ('-h', '--help'): |
| 118 self.usage_and_exit(0) | 116 self.usage_and_exit(0) |
| 119 if self.version is not None and arg in ('--version'): | 117 if self.version is not None and arg in ('--version'): |
| 120 print(self.version) | 118 print self.version |
| 121 sys.exit(0) | 119 sys.exit(0) |
| 122 rcfile = self.rcfile | 120 rcfile = self.rcfile |
| 123 if rcfile is not None and arg in ('-C', '--rc-file'): | 121 if rcfile is not None and arg in ('-C', '--rc-file'): |
| 124 try: | 122 try: |
| 125 rcfile = args.pop(0) | 123 rcfile = args.pop(0) |
| 126 arg = args.pop(0) | 124 arg = args.pop(0) |
| 127 except IndexError: | 125 except IndexError: |
| 128 self.usage_and_exit(1) | 126 self.usage_and_exit(1) |
| 129 try: | 127 try: |
| 130 command = self.get_command(arg) | 128 command = self.get_command(arg) |
| 131 except KeyError: | 129 except KeyError: |
| 132 print('ERROR: no %s command' % arg) | 130 print 'ERROR: no %s command' % arg |
| 133 print() | 131 print |
| 134 self.usage_and_exit(1) | 132 self.usage_and_exit(1) |
| 135 try: | 133 try: |
| 136 sys.exit(command.main_run(args, rcfile)) | 134 sys.exit(command.main_run(args, rcfile)) |
| 137 except KeyboardInterrupt as exc: | 135 except KeyboardInterrupt, exc: |
| 138 print('Interrupted', end=' ') | 136 print 'Interrupted', |
| 139 if str(exc): | 137 if str(exc): |
| 140 print(': %s' % exc, end=' ') | 138 print ': %s' % exc, |
| 141 print() | 139 print |
| 142 sys.exit(4) | 140 sys.exit(4) |
| 143 except BadCommandUsage as err: | 141 except BadCommandUsage, err: |
| 144 print('ERROR:', err) | 142 print 'ERROR:', err |
| 145 print() | 143 print |
| 146 print(command.help()) | 144 print command.help() |
| 147 sys.exit(1) | 145 sys.exit(1) |
| 148 | 146 |
| 149 def create_logger(self, handler, logthreshold=None): | 147 def create_logger(self, handler, logthreshold=None): |
| 150 logger = logging.Logger(self.pgm) | 148 logger = logging.Logger(self.pgm) |
| 151 logger.handlers = [handler] | 149 logger.handlers = [handler] |
| 152 if logthreshold is None: | 150 if logthreshold is None: |
| 153 logthreshold = get_threshold(self.logthreshold) | 151 logthreshold = get_threshold(self.logthreshold) |
| 154 logger.setLevel(logthreshold) | 152 logger.setLevel(logthreshold) |
| 155 return logger | 153 return logger |
| 156 | 154 |
| 157 def get_command(self, cmd, logger=None): | 155 def get_command(self, cmd, logger=None): |
| 158 if logger is None: | 156 if logger is None: |
| 159 logger = self.logger | 157 logger = self.logger |
| 160 if logger is None: | 158 if logger is None: |
| 161 logger = self.logger = logging.getLogger(self.pgm) | 159 logger = self.logger = logging.getLogger(self.pgm) |
| 162 logger.setLevel(get_threshold(self.logthreshold)) | 160 logger.setLevel(get_threshold(self.logthreshold)) |
| 163 return self[cmd](logger) | 161 return self[cmd](logger) |
| 164 | 162 |
| 165 def usage(self): | 163 def usage(self): |
| 166 """display usage for the main program (i.e. when no command supplied) | 164 """display usage for the main program (i.e. when no command supplied) |
| 167 and exit | 165 and exit |
| 168 """ | 166 """ |
| 169 print('usage:', self.pgm, end=' ') | 167 print 'usage:', self.pgm, |
| 170 if self.rcfile: | 168 if self.rcfile: |
| 171 print('[--rc-file=<configuration file>]', end=' ') | 169 print '[--rc-file=<configuration file>]', |
| 172 print('<command> [options] <command argument>...') | 170 print '<command> [options] <command argument>...' |
| 173 if self.doc: | 171 if self.doc: |
| 174 print('\n%s' % self.doc) | 172 print '\n%s' % self.doc |
| 175 print(''' | 173 print ''' |
| 176 Type "%(pgm)s <command> --help" for more information about a specific | 174 Type "%(pgm)s <command> --help" for more information about a specific |
| 177 command. Available commands are :\n''' % self.__dict__) | 175 command. Available commands are :\n''' % self.__dict__ |
| 178 max_len = max([len(cmd) for cmd in self]) | 176 max_len = max([len(cmd) for cmd in self]) |
| 179 padding = ' ' * max_len | 177 padding = ' ' * max_len |
| 180 for cmdname, cmd in sorted(self.items()): | 178 for cmdname, cmd in sorted(self.items()): |
| 181 if not cmd.hidden: | 179 if not cmd.hidden: |
| 182 print(' ', (cmdname + padding)[:max_len], cmd.short_description(
)) | 180 print ' ', (cmdname + padding)[:max_len], cmd.short_description(
) |
| 183 if self.rcfile: | 181 if self.rcfile: |
| 184 print(''' | 182 print ''' |
| 185 Use --rc-file=<configuration file> / -C <configuration file> before the command | 183 Use --rc-file=<configuration file> / -C <configuration file> before the command |
| 186 to specify a configuration file. Default to %s. | 184 to specify a configuration file. Default to %s. |
| 187 ''' % self.rcfile) | 185 ''' % self.rcfile |
| 188 print('''%(pgm)s -h/--help | 186 print '''%(pgm)s -h/--help |
| 189 display this usage information and exit''' % self.__dict__) | 187 display this usage information and exit''' % self.__dict__ |
| 190 if self.version: | 188 if self.version: |
| 191 print('''%(pgm)s -v/--version | 189 print '''%(pgm)s -v/--version |
| 192 display version configuration and exit''' % self.__dict__) | 190 display version configuration and exit''' % self.__dict__ |
| 193 if self.copyright: | 191 if self.copyright: |
| 194 print('\n', self.copyright) | 192 print '\n', self.copyright |
| 195 | 193 |
| 196 def usage_and_exit(self, status): | 194 def usage_and_exit(self, status): |
| 197 self.usage() | 195 self.usage() |
| 198 sys.exit(status) | 196 sys.exit(status) |
| 199 | 197 |
| 200 | 198 |
| 201 # base command classes ######################################################### | 199 # base command classes ######################################################### |
| 202 | 200 |
| 203 class Command(Configuration): | 201 class Command(Configuration): |
| 204 """Base class for command line commands. | 202 """Base class for command line commands. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 | 254 |
| 257 Any other exceptions, including :exc:`BadCommandUsage` will be | 255 Any other exceptions, including :exc:`BadCommandUsage` will be |
| 258 propagated. | 256 propagated. |
| 259 """ | 257 """ |
| 260 if rcfile: | 258 if rcfile: |
| 261 self.load_file_configuration(rcfile) | 259 self.load_file_configuration(rcfile) |
| 262 args = self.load_command_line_configuration(args) | 260 args = self.load_command_line_configuration(args) |
| 263 try: | 261 try: |
| 264 self.check_args(args) | 262 self.check_args(args) |
| 265 self.run(args) | 263 self.run(args) |
| 266 except CommandError as err: | 264 except CommandError, err: |
| 267 self.logger.error(err) | 265 self.logger.error(err) |
| 268 return 2 | 266 return 2 |
| 269 return 0 | 267 return 0 |
| 270 | 268 |
| 271 def run(self, args): | 269 def run(self, args): |
| 272 """run the command with its specific arguments""" | 270 """run the command with its specific arguments""" |
| 273 raise NotImplementedError() | 271 raise NotImplementedError() |
| 274 | 272 |
| 275 | 273 |
| 276 class ListCommandsCommand(Command): | 274 class ListCommandsCommand(Command): |
| 277 """list available commands, useful for bash completion.""" | 275 """list available commands, useful for bash completion.""" |
| 278 name = 'listcommands' | 276 name = 'listcommands' |
| 279 arguments = '[command]' | 277 arguments = '[command]' |
| 280 hidden = True | 278 hidden = True |
| 281 | 279 |
| 282 def run(self, args): | 280 def run(self, args): |
| 283 """run the command with its specific arguments""" | 281 """run the command with its specific arguments""" |
| 284 if args: | 282 if args: |
| 285 command = args.pop() | 283 command = args.pop() |
| 286 cmd = _COMMANDS[command] | 284 cmd = _COMMANDS[command] |
| 287 for optname, optdict in cmd.options: | 285 for optname, optdict in cmd.options: |
| 288 print('--help') | 286 print '--help' |
| 289 print('--' + optname) | 287 print '--' + optname |
| 290 else: | 288 else: |
| 291 commands = sorted(_COMMANDS.keys()) | 289 commands = sorted(_COMMANDS.keys()) |
| 292 for command in commands: | 290 for command in commands: |
| 293 cmd = _COMMANDS[command] | 291 cmd = _COMMANDS[command] |
| 294 if not cmd.hidden: | 292 if not cmd.hidden: |
| 295 print(command) | 293 print command |
| 296 | 294 |
| 297 | 295 |
| 298 # deprecated stuff ############################################################# | 296 # deprecated stuff ############################################################# |
| 299 | 297 |
| 300 _COMMANDS = CommandLine() | 298 _COMMANDS = CommandLine() |
| 301 | 299 |
| 302 DEFAULT_COPYRIGHT = '''\ | 300 DEFAULT_COPYRIGHT = '''\ |
| 303 Copyright (c) 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | 301 Copyright (c) 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 304 http://www.logilab.fr/ -- mailto:contact@logilab.fr''' | 302 http://www.logilab.fr/ -- mailto:contact@logilab.fr''' |
| 305 | 303 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 325 def pop_arg(args_list, expected_size_after=None, msg="Missing argument"): | 323 def pop_arg(args_list, expected_size_after=None, msg="Missing argument"): |
| 326 """helper function to get and check command line arguments""" | 324 """helper function to get and check command line arguments""" |
| 327 try: | 325 try: |
| 328 value = args_list.pop(0) | 326 value = args_list.pop(0) |
| 329 except IndexError: | 327 except IndexError: |
| 330 raise BadCommandUsage(msg) | 328 raise BadCommandUsage(msg) |
| 331 if expected_size_after is not None and len(args_list) > expected_size_after: | 329 if expected_size_after is not None and len(args_list) > expected_size_after: |
| 332 raise BadCommandUsage('too many arguments') | 330 raise BadCommandUsage('too many arguments') |
| 333 return value | 331 return value |
| 334 | 332 |
| OLD | NEW |