Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(999)

Side by Side Diff: third_party/logilab/logilab/common/clcommands.py

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # copyright 2003-2011 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 unde r
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
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 from __future__ import print_function
26
27 __docformat__ = "restructuredtext en"
28
29 import sys
30 import logging
31 from os.path import basename
32
33 from logilab.common.configuration import Configuration
34 from logilab.common.logging_ext import init_log, get_threshold
35 from logilab.common.deprecation import deprecated
36
37
38 class BadCommandUsage(Exception):
39 """Raised when an unknown command is used or when a command is not
40 correctly used (bad options, too much / missing arguments...).
41
42 Trigger display of command usage.
43 """
44
45 class CommandError(Exception):
46 """Raised when a command can't be processed and we want to display it and
47 exit, without traceback nor usage displayed.
48 """
49
50
51 # command line access point ####################################################
52
53 class CommandLine(dict):
54 """Usage:
55
56 >>> LDI = cli.CommandLine('ldi', doc='Logilab debian installer',
57 version=version, rcfile=RCFILE)
58 >>> LDI.register(MyCommandClass)
59 >>> LDI.register(MyOtherCommandClass)
60 >>> LDI.run(sys.argv[1:])
61
62 Arguments:
63
64 * `pgm`, the program name, default to `basename(sys.argv[0])`
65
66 * `doc`, a short description of the command line tool
67
68 * `copyright`, additional doc string that will be appended to the generated
69 doc
70
71 * `version`, version number of string of the tool. If specified, global
72 --version option will be available.
73
74 * `rcfile`, path to a configuration file. If specified, global --C/--rc-file
75 option will be available? self.rcfile = rcfile
76
77 * `logger`, logger to propagate to commands, default to
78 `logging.getLogger(self.pgm))`
79 """
80 def __init__(self, pgm=None, doc=None, copyright=None, version=None,
81 rcfile=None, logthreshold=logging.ERROR,
82 check_duplicated_command=True):
83 if pgm is None:
84 pgm = basename(sys.argv[0])
85 self.pgm = pgm
86 self.doc = doc
87 self.copyright = copyright
88 self.version = version
89 self.rcfile = rcfile
90 self.logger = None
91 self.logthreshold = logthreshold
92 self.check_duplicated_command = check_duplicated_command
93
94 def register(self, cls, force=False):
95 """register the given :class:`Command` subclass"""
96 assert not self.check_duplicated_command or force or not cls.name in sel f, \
97 'a command %s is already defined' % cls.name
98 self[cls.name] = cls
99 return cls
100
101 def run(self, args):
102 """main command line access point:
103 * init logging
104 * handle global options (-h/--help, --version, -C/--rc-file)
105 * check command
106 * run command
107
108 Terminate by :exc:`SystemExit`
109 """
110 init_log(debug=True, # so that we use StreamHandler
111 logthreshold=self.logthreshold,
112 logformat='%(levelname)s: %(message)s')
113 try:
114 arg = args.pop(0)
115 except IndexError:
116 self.usage_and_exit(1)
117 if arg in ('-h', '--help'):
118 self.usage_and_exit(0)
119 if self.version is not None and arg in ('--version'):
120 print(self.version)
121 sys.exit(0)
122 rcfile = self.rcfile
123 if rcfile is not None and arg in ('-C', '--rc-file'):
124 try:
125 rcfile = args.pop(0)
126 arg = args.pop(0)
127 except IndexError:
128 self.usage_and_exit(1)
129 try:
130 command = self.get_command(arg)
131 except KeyError:
132 print('ERROR: no %s command' % arg)
133 print()
134 self.usage_and_exit(1)
135 try:
136 sys.exit(command.main_run(args, rcfile))
137 except KeyboardInterrupt as exc:
138 print('Interrupted', end=' ')
139 if str(exc):
140 print(': %s' % exc, end=' ')
141 print()
142 sys.exit(4)
143 except BadCommandUsage as err:
144 print('ERROR:', err)
145 print()
146 print(command.help())
147 sys.exit(1)
148
149 def create_logger(self, handler, logthreshold=None):
150 logger = logging.Logger(self.pgm)
151 logger.handlers = [handler]
152 if logthreshold is None:
153 logthreshold = get_threshold(self.logthreshold)
154 logger.setLevel(logthreshold)
155 return logger
156
157 def get_command(self, cmd, logger=None):
158 if logger is None:
159 logger = self.logger
160 if logger is None:
161 logger = self.logger = logging.getLogger(self.pgm)
162 logger.setLevel(get_threshold(self.logthreshold))
163 return self[cmd](logger)
164
165 def usage(self):
166 """display usage for the main program (i.e. when no command supplied)
167 and exit
168 """
169 print('usage:', self.pgm, end=' ')
170 if self.rcfile:
171 print('[--rc-file=<configuration file>]', end=' ')
172 print('<command> [options] <command argument>...')
173 if self.doc:
174 print('\n%s' % self.doc)
175 print('''
176 Type "%(pgm)s <command> --help" for more information about a specific
177 command. Available commands are :\n''' % self.__dict__)
178 max_len = max([len(cmd) for cmd in self])
179 padding = ' ' * max_len
180 for cmdname, cmd in sorted(self.items()):
181 if not cmd.hidden:
182 print(' ', (cmdname + padding)[:max_len], cmd.short_description( ))
183 if self.rcfile:
184 print('''
185 Use --rc-file=<configuration file> / -C <configuration file> before the command
186 to specify a configuration file. Default to %s.
187 ''' % self.rcfile)
188 print('''%(pgm)s -h/--help
189 display this usage information and exit''' % self.__dict__)
190 if self.version:
191 print('''%(pgm)s -v/--version
192 display version configuration and exit''' % self.__dict__)
193 if self.copyright:
194 print('\n', self.copyright)
195
196 def usage_and_exit(self, status):
197 self.usage()
198 sys.exit(status)
199
200
201 # base command classes #########################################################
202
203 class Command(Configuration):
204 """Base class for command line commands.
205
206 Class attributes:
207
208 * `name`, the name of the command
209
210 * `min_args`, minimum number of arguments, None if unspecified
211
212 * `max_args`, maximum number of arguments, None if unspecified
213
214 * `arguments`, string describing arguments, used in command usage
215
216 * `hidden`, boolean flag telling if the command should be hidden, e.g. does
217 not appear in help's commands list
218
219 * `options`, options list, as allowed by :mod:configuration
220 """
221
222 arguments = ''
223 name = ''
224 # hidden from help ?
225 hidden = False
226 # max/min args, None meaning unspecified
227 min_args = None
228 max_args = None
229
230 @classmethod
231 def description(cls):
232 return cls.__doc__.replace(' ', '')
233
234 @classmethod
235 def short_description(cls):
236 return cls.description().split('.')[0]
237
238 def __init__(self, logger):
239 usage = '%%prog %s %s\n\n%s' % (self.name, self.arguments,
240 self.description())
241 Configuration.__init__(self, usage=usage)
242 self.logger = logger
243
244 def check_args(self, args):
245 """check command's arguments are provided"""
246 if self.min_args is not None and len(args) < self.min_args:
247 raise BadCommandUsage('missing argument')
248 if self.max_args is not None and len(args) > self.max_args:
249 raise BadCommandUsage('too many arguments')
250
251 def main_run(self, args, rcfile=None):
252 """Run the command and return status 0 if everything went fine.
253
254 If :exc:`CommandError` is raised by the underlying command, simply log
255 the error and return status 2.
256
257 Any other exceptions, including :exc:`BadCommandUsage` will be
258 propagated.
259 """
260 if rcfile:
261 self.load_file_configuration(rcfile)
262 args = self.load_command_line_configuration(args)
263 try:
264 self.check_args(args)
265 self.run(args)
266 except CommandError as err:
267 self.logger.error(err)
268 return 2
269 return 0
270
271 def run(self, args):
272 """run the command with its specific arguments"""
273 raise NotImplementedError()
274
275
276 class ListCommandsCommand(Command):
277 """list available commands, useful for bash completion."""
278 name = 'listcommands'
279 arguments = '[command]'
280 hidden = True
281
282 def run(self, args):
283 """run the command with its specific arguments"""
284 if args:
285 command = args.pop()
286 cmd = _COMMANDS[command]
287 for optname, optdict in cmd.options:
288 print('--help')
289 print('--' + optname)
290 else:
291 commands = sorted(_COMMANDS.keys())
292 for command in commands:
293 cmd = _COMMANDS[command]
294 if not cmd.hidden:
295 print(command)
296
297
298 # deprecated stuff #############################################################
299
300 _COMMANDS = CommandLine()
301
302 DEFAULT_COPYRIGHT = '''\
303 Copyright (c) 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
304 http://www.logilab.fr/ -- mailto:contact@logilab.fr'''
305
306 @deprecated('use cls.register(cli)')
307 def register_commands(commands):
308 """register existing commands"""
309 for command_klass in commands:
310 _COMMANDS.register(command_klass)
311
312 @deprecated('use args.pop(0)')
313 def main_run(args, doc=None, copyright=None, version=None):
314 """command line tool: run command specified by argument list (without the
315 program name). Raise SystemExit with status 0 if everything went fine.
316
317 >>> main_run(sys.argv[1:])
318 """
319 _COMMANDS.doc = doc
320 _COMMANDS.copyright = copyright
321 _COMMANDS.version = version
322 _COMMANDS.run(args)
323
324 @deprecated('use args.pop(0)')
325 def pop_arg(args_list, expected_size_after=None, msg="Missing argument"):
326 """helper function to get and check command line arguments"""
327 try:
328 value = args_list.pop(0)
329 except IndexError:
330 raise BadCommandUsage(msg)
331 if expected_size_after is not None and len(args_list) > expected_size_after:
332 raise BadCommandUsage('too many arguments')
333 return value
334
OLDNEW
« no previous file with comments | « third_party/logilab/logilab/common/changelog.py ('k') | third_party/logilab/logilab/common/cli.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698