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

Side by Side Diff: third_party/twisted_8_1/twisted/python/usage.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.test.test_usage -*-
2 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 twisted.python.usage is a module for parsing/handling the
8 command line of your program.
9
10 For information on how to use it, see
11 U{http://twistedmatrix.com/projects/core/documentation/howto/options.html},
12 or doc/howto/options.html in your Twisted directory.
13 """
14
15 # System Imports
16 import os
17 import sys
18 import getopt
19 from os import path
20
21 # Sibling Imports
22 from twisted.python import reflect, text, util
23
24
25 class UsageError(Exception):
26 pass
27
28
29 error = UsageError
30
31
32 class CoerceParameter(object):
33 """
34 Utility class that can corce a parameter before storing it.
35 """
36 def __init__(self, options, coerce):
37 """
38 @param options: parent Options object
39 @param coerce: callable used to coerce the value.
40 """
41 self.options = options
42 self.coerce = coerce
43 self.doc = getattr(self.coerce, 'coerceDoc', '')
44
45 def dispatch(self, parameterName, value):
46 """
47 When called in dispatch, do the coerce for C{value} and save the
48 returned value.
49 """
50 if value is None:
51 raise UsageError("Parameter '%s' requires an argument."
52 % (parameterName,))
53 try:
54 value = self.coerce(value)
55 except ValueError, e:
56 raise UsageError("Parameter type enforcement failed: %s" % (e,))
57
58 self.options.opts[parameterName] = value
59
60
61 class Options(dict):
62 """
63 An option list parser class
64
65 C{optFlags} and C{optParameters} are lists of available parameters
66 which your program can handle. The difference between the two
67 is the 'flags' have an on(1) or off(0) state (off by default)
68 whereas 'parameters' have an assigned value, with an optional
69 default. (Compare '--verbose' and '--verbosity=2')
70
71 optFlags is assigned a list of lists. Each list represents
72 a flag parameter, as so::
73
74 | optFlags = [['verbose', 'v', 'Makes it tell you what it doing.'],
75 | ['quiet', 'q', 'Be vewy vewy quiet.']]
76
77 As you can see, the first item is the long option name
78 (prefixed with '--' on the command line), followed by the
79 short option name (prefixed with '-'), and the description.
80 The description is used for the built-in handling of the
81 --help switch, which prints a usage summary.
82
83 C{optParameters} is much the same, except the list also contains
84 a default value::
85
86 | optParameters = [['outfile', 'O', 'outfile.log', 'Description...']]
87
88 A coerce function can also be specified as the last element: it will be
89 called with the argument and should return the value that will be stored
90 for the option. This function can have a C{coerceDoc} attribute which
91 will be appended to the documentation of the option.
92
93 subCommands is a list of 4-tuples of (command name, command shortcut,
94 parser class, documentation). If the first non-option argument found is
95 one of the given command names, an instance of the given parser class is
96 instantiated and given the remainder of the arguments to parse and
97 self.opts[command] is set to the command name. For example::
98
99 | subCommands = [
100 | ['inquisition', 'inquest', InquisitionOptions,
101 | 'Perform an inquisition'],
102 | ['holyquest', 'quest', HolyQuestOptions,
103 | 'Embark upon a holy quest']
104 | ]
105
106 In this case, C{"<program> holyquest --horseback --for-grail"} will cause
107 C{HolyQuestOptions} to be instantiated and asked to parse
108 C{['--horseback', '--for-grail']}. Currently, only the first sub-command
109 is parsed, and all options following it are passed to its parser. If a
110 subcommand is found, the subCommand attribute is set to its name and the
111 subOptions attribute is set to the Option instance that parses the
112 remaining options. If a subcommand is not given to parseOptions,
113 the subCommand attribute will be None. You can also mark one of
114 the subCommands to be the default.
115
116 | defaultSubCommand = 'holyquest'
117
118 In this case, the subCommand attribute will never be None, and
119 the subOptions attribute will always be set.
120
121 If you want to handle your own options, define a method named
122 C{opt_paramname} that takes C{(self, option)} as arguments. C{option}
123 will be whatever immediately follows the parameter on the
124 command line. Options fully supports the mapping interface, so you
125 can do things like C{'self["option"] = val'} in these methods.
126
127 Advanced functionality is covered in the howto documentation,
128 available at
129 U{http://twistedmatrix.com/projects/core/documentation/howto/options.html},
130 or doc/howto/options.html in your Twisted directory.
131 """
132
133 subCommand = None
134 defaultSubCommand = None
135 parent = None
136 def __init__(self):
137 super(Options, self).__init__()
138
139 self.opts = self
140 self.defaults = {}
141
142 # These are strings/lists we will pass to getopt
143 self.longOpt = []
144 self.shortOpt = ''
145 self.docs = {}
146 self.synonyms = {}
147 self._dispatch = {}
148
149
150 collectors = [
151 self._gather_flags,
152 self._gather_parameters,
153 self._gather_handlers,
154 ]
155
156 for c in collectors:
157 (longOpt, shortOpt, docs, settings, synonyms, dispatch) = c()
158 self.longOpt.extend(longOpt)
159 self.shortOpt = self.shortOpt + shortOpt
160 self.docs.update(docs)
161
162 self.opts.update(settings)
163 self.defaults.update(settings)
164
165 self.synonyms.update(synonyms)
166 self._dispatch.update(dispatch)
167
168 def __hash__(self):
169 """
170 Define a custom hash function so that Options instances can be used
171 as dictionary keys. This is an internal feature used to implement
172 the parser. Do not rely on it in application code.
173 """
174 return int(id(self) % sys.maxint)
175
176 def opt_help(self):
177 """
178 Display this help and exit.
179 """
180 print self.__str__()
181 sys.exit(0)
182
183 def opt_version(self):
184 from twisted import copyright
185 print "Twisted version:", copyright.version
186 sys.exit(0)
187
188 #opt_h = opt_help # this conflicted with existing 'host' options.
189
190 def parseOptions(self, options=None):
191 """
192 The guts of the command-line parser.
193 """
194
195 if options is None:
196 options = sys.argv[1:]
197 try:
198 opts, args = getopt.getopt(options,
199 self.shortOpt, self.longOpt)
200 except getopt.error, e:
201 raise UsageError(str(e))
202
203 for opt, arg in opts:
204 if opt[1] == '-':
205 opt = opt[2:]
206 else:
207 opt = opt[1:]
208
209 optMangled = opt
210 if optMangled not in self.synonyms:
211 optMangled = opt.replace("-", "_")
212 if optMangled not in self.synonyms:
213 raise UsageError("No such option '%s'" % (opt,))
214
215 optMangled = self.synonyms[optMangled]
216 if isinstance(self._dispatch[optMangled], CoerceParameter):
217 self._dispatch[optMangled].dispatch(optMangled, arg)
218 else:
219 self._dispatch[optMangled](optMangled, arg)
220
221 if (getattr(self, 'subCommands', None)
222 and (args or self.defaultSubCommand is not None)):
223 if not args:
224 args = [self.defaultSubCommand]
225 sub, rest = args[0], args[1:]
226 for (cmd, short, parser, doc) in self.subCommands:
227 if sub == cmd or sub == short:
228 self.subCommand = cmd
229 self.subOptions = parser()
230 self.subOptions.parent = self
231 self.subOptions.parseOptions(rest)
232 break
233 else:
234 raise UsageError("Unknown command: %s" % sub)
235 else:
236 try:
237 self.parseArgs(*args)
238 except TypeError:
239 raise UsageError("Wrong number of arguments.")
240
241 self.postOptions()
242
243 def postOptions(self):
244 """
245 I am called after the options are parsed.
246
247 Override this method in your subclass to do something after
248 the options have been parsed and assigned, like validate that
249 all options are sane.
250 """
251
252 def parseArgs(self):
253 """
254 I am called with any leftover arguments which were not options.
255
256 Override me to do something with the remaining arguments on
257 the command line, those which were not flags or options. e.g.
258 interpret them as a list of files to operate on.
259
260 Note that if there more arguments on the command line
261 than this method accepts, parseArgs will blow up with
262 a getopt.error. This means if you don't override me,
263 parseArgs will blow up if I am passed any arguments at
264 all!
265 """
266
267 def _generic_flag(self, flagName, value=None):
268 if value not in ('', None):
269 raise UsageError("Flag '%s' takes no argument."
270 " Not even \"%s\"." % (flagName, value))
271
272 self.opts[flagName] = 1
273
274 def _gather_flags(self):
275 """
276 Gather up boolean (flag) options.
277 """
278
279 longOpt, shortOpt = [], ''
280 docs, settings, synonyms, dispatch = {}, {}, {}, {}
281
282 flags = []
283 reflect.accumulateClassList(self.__class__, 'optFlags', flags)
284
285 for flag in flags:
286 long, short, doc = util.padTo(3, flag)
287 if not long:
288 raise ValueError("A flag cannot be without a name.")
289
290 docs[long] = doc
291 settings[long] = 0
292 if short:
293 shortOpt = shortOpt + short
294 synonyms[short] = long
295 longOpt.append(long)
296 synonyms[long] = long
297 dispatch[long] = self._generic_flag
298
299 return longOpt, shortOpt, docs, settings, synonyms, dispatch
300
301 def _gather_parameters(self):
302 """
303 Gather options which take a value.
304 """
305 longOpt, shortOpt = [], ''
306 docs, settings, synonyms, dispatch = {}, {}, {}, {}
307
308 parameters = []
309
310 reflect.accumulateClassList(self.__class__, 'optStrings',
311 parameters)
312 if parameters:
313 import warnings
314 warnings.warn("Options.optStrings is deprecated, "
315 "please use optParameters instead.", stacklevel=2)
316
317 reflect.accumulateClassList(self.__class__, 'optParameters',
318 parameters)
319
320 synonyms = {}
321
322 for parameter in parameters:
323 long, short, default, doc, paramType = util.padTo(5, parameter)
324 if not long:
325 raise ValueError("A parameter cannot be without a name.")
326
327 docs[long] = doc
328 settings[long] = default
329 if short:
330 shortOpt = shortOpt + short + ':'
331 synonyms[short] = long
332 longOpt.append(long + '=')
333 synonyms[long] = long
334 if paramType is not None:
335 dispatch[long] = CoerceParameter(self, paramType)
336 else:
337 dispatch[long] = CoerceParameter(self, str)
338
339 return longOpt, shortOpt, docs, settings, synonyms, dispatch
340
341
342 def _gather_handlers(self):
343 """
344 Gather up options with their own handler methods.
345 """
346
347 longOpt, shortOpt = [], ''
348 docs, settings, synonyms, dispatch = {}, {}, {}, {}
349
350 dct = {}
351 reflect.addMethodNamesToDict(self.__class__, dct, "opt_")
352
353 for name in dct.keys():
354 method = getattr(self, 'opt_'+name)
355
356 takesArg = not flagFunction(method, name)
357
358 prettyName = name.replace('_', '-')
359 doc = getattr(method, '__doc__', None)
360 if doc:
361 ## Only use the first line.
362 #docs[name] = doc.split('\n')[0]
363 docs[prettyName] = doc
364 else:
365 docs[prettyName] = self.docs.get(prettyName)
366
367 synonyms[prettyName] = prettyName
368
369 # A little slight-of-hand here makes dispatching much easier
370 # in parseOptions, as it makes all option-methods have the
371 # same signature.
372 if takesArg:
373 fn = lambda name, value, m=method: m(value)
374 else:
375 # XXX: This won't raise a TypeError if it's called
376 # with a value when it shouldn't be.
377 fn = lambda name, value=None, m=method: m()
378
379 dispatch[prettyName] = fn
380
381 if len(name) == 1:
382 shortOpt = shortOpt + name
383 if takesArg:
384 shortOpt = shortOpt + ':'
385 else:
386 if takesArg:
387 prettyName = prettyName + '='
388 longOpt.append(prettyName)
389
390 reverse_dct = {}
391 # Map synonyms
392 for name in dct.keys():
393 method = getattr(self, 'opt_' + name)
394 if method not in reverse_dct:
395 reverse_dct[method] = []
396 reverse_dct[method].append(name)
397
398 cmpLength = lambda a, b: cmp(len(a), len(b))
399
400 for method, names in reverse_dct.items():
401 if len(names) < 2:
402 continue
403 names_ = names[:]
404 names_.sort(cmpLength)
405 longest = names_.pop()
406 for name in names_:
407 synonyms[name] = longest
408
409 return longOpt, shortOpt, docs, settings, synonyms, dispatch
410
411
412 def __str__(self):
413 return self.getSynopsis() + '\n' + self.getUsage(width=None)
414
415 def getSynopsis(self):
416 """
417 Returns a string containing a description of these options and how to
418 pass them to the executed file.
419 """
420
421 default = "%s%s" % (path.basename(sys.argv[0]),
422 (self.longOpt and " [options]") or '')
423 if self.parent is None:
424 default = "Usage: %s%s" % (path.basename(sys.argv[0]),
425 (self.longOpt and " [options]") or '')
426 else:
427 default = '%s' % ((self.longOpt and "[options]") or '')
428 synopsis = getattr(self, "synopsis", default)
429
430 synopsis = synopsis.rstrip()
431
432 if self.parent is not None:
433 synopsis = ' '.join((self.parent.getSynopsis(),
434 self.parent.subCommand, synopsis))
435
436 return synopsis
437
438 def getUsage(self, width=None):
439 # If subOptions exists by now, then there was probably an error while
440 # parsing its options.
441 if hasattr(self, 'subOptions'):
442 return self.subOptions.getUsage(width=width)
443
444 if not width:
445 width = int(os.environ.get('COLUMNS', '80'))
446
447 if hasattr(self, 'subCommands'):
448 cmdDicts = []
449 for (cmd, short, parser, desc) in self.subCommands:
450 cmdDicts.append(
451 {'long': cmd,
452 'short': short,
453 'doc': desc,
454 'optType': 'command',
455 'default': None
456 })
457 chunks = docMakeChunks(cmdDicts, width)
458 commands = 'Commands:\n' + ''.join(chunks)
459 else:
460 commands = ''
461
462 longToShort = {}
463 for key, value in self.synonyms.items():
464 longname = value
465 if (key != longname) and (len(key) == 1):
466 longToShort[longname] = key
467 else:
468 if longname not in longToShort:
469 longToShort[longname] = None
470 else:
471 pass
472
473 optDicts = []
474 for opt in self.longOpt:
475 if opt[-1] == '=':
476 optType = 'parameter'
477 opt = opt[:-1]
478 else:
479 optType = 'flag'
480
481 optDicts.append(
482 {'long': opt,
483 'short': longToShort[opt],
484 'doc': self.docs[opt],
485 'optType': optType,
486 'default': self.defaults.get(opt, None),
487 'dispatch': self._dispatch.get(opt, None)
488 })
489
490 if not (getattr(self, "longdesc", None) is None):
491 longdesc = self.longdesc
492 else:
493 import __main__
494 if getattr(__main__, '__doc__', None):
495 longdesc = __main__.__doc__
496 else:
497 longdesc = ''
498
499 if longdesc:
500 longdesc = ('\n' +
501 '\n'.join(text.wordWrap(longdesc, width)).strip()
502 + '\n')
503
504 if optDicts:
505 chunks = docMakeChunks(optDicts, width)
506 s = "Options:\n%s" % (''.join(chunks))
507 else:
508 s = "Options: None\n"
509
510 return s + longdesc + commands
511
512 #def __repr__(self):
513 # XXX: It'd be cool if we could return a succinct representation
514 # of which flags and options are set here.
515
516
517 def docMakeChunks(optList, width=80):
518 """
519 Makes doc chunks for option declarations.
520
521 Takes a list of dictionaries, each of which may have one or more
522 of the keys 'long', 'short', 'doc', 'default', 'optType'.
523
524 Returns a list of strings.
525 The strings may be multiple lines,
526 all of them end with a newline.
527 """
528
529 # XXX: sanity check to make sure we have a sane combination of keys.
530
531 maxOptLen = 0
532 for opt in optList:
533 optLen = len(opt.get('long', ''))
534 if optLen:
535 if opt.get('optType', None) == "parameter":
536 # these take up an extra character
537 optLen = optLen + 1
538 maxOptLen = max(optLen, maxOptLen)
539
540 colWidth1 = maxOptLen + len(" -s, -- ")
541 colWidth2 = width - colWidth1
542 # XXX - impose some sane minimum limit.
543 # Then if we don't have enough room for the option and the doc
544 # to share one line, they can take turns on alternating lines.
545
546 colFiller1 = " " * colWidth1
547
548 optChunks = []
549 seen = {}
550 for opt in optList:
551 if opt.get('short', None) in seen or opt.get('long', None) in seen:
552 continue
553 for x in opt.get('short', None), opt.get('long', None):
554 if x is not None:
555 seen[x] = 1
556
557 optLines = []
558 comma = " "
559 if opt.get('short', None):
560 short = "-%c" % (opt['short'],)
561 else:
562 short = ''
563
564 if opt.get('long', None):
565 long = opt['long']
566 if opt.get("optType", None) == "parameter":
567 long = long + '='
568
569 long = "%-*s" % (maxOptLen, long)
570 if short:
571 comma = ","
572 else:
573 long = " " * (maxOptLen + len('--'))
574
575 if opt.get('optType', None) == 'command':
576 column1 = ' %s ' % long
577 else:
578 column1 = " %2s%c --%s " % (short, comma, long)
579
580 if opt.get('doc', ''):
581 doc = opt['doc'].strip()
582 else:
583 doc = ''
584
585 if (opt.get("optType", None) == "parameter") \
586 and not (opt.get('default', None) is None):
587 doc = "%s [default: %s]" % (doc, opt['default'])
588
589 if (opt.get("optType", None) == "parameter") \
590 and opt.get('dispatch', None) is not None:
591 d = opt['dispatch']
592 if isinstance(d, CoerceParameter) and d.doc:
593 doc = "%s. %s" % (doc, d.doc)
594
595 if doc:
596 column2_l = text.wordWrap(doc, colWidth2)
597 else:
598 column2_l = ['']
599
600 optLines.append("%s%s\n" % (column1, column2_l.pop(0)))
601
602 for line in column2_l:
603 optLines.append("%s%s\n" % (colFiller1, line))
604
605 optChunks.append(''.join(optLines))
606
607 return optChunks
608
609
610 def flagFunction(method, name=None):
611 reqArgs = method.im_func.func_code.co_argcount
612 if reqArgs > 2:
613 raise UsageError('Invalid Option function for %s' %
614 (name or method.func_name))
615 if reqArgs == 2:
616 # argName = method.im_func.func_code.co_varnames[1]
617 return 0
618 return 1
619
620
621 def portCoerce(value):
622 """
623 Coerce a string value to an int port number, and checks the validity.
624 """
625 value = int(value)
626 if value < 0 or value > 65535:
627 raise ValueError("Port number not in range: %s" % (value,))
628 return value
629 portCoerce.coerceDoc = "Must be an int between 0 and 65535."
630
631
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/python/urlpath.py ('k') | third_party/twisted_8_1/twisted/python/util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698