Using FS mtime to reload non recursive cache.
[pyTivo.git] / Cheetah / Utils / optik / option_parser.py
blob1b4e6328038555d72ab76ced49ed732307db4e11
1 """optik.option_parser
3 Provides the OptionParser and Values classes.
5 Cheetah modifications: added "Cheetah.Utils.optik." prefix to
6 all intra-Optik imports.
7 """
9 __revision__ = "$Id: option_parser.py,v 1.2 2002/09/12 06:56:51 hierro Exp $"
11 # Copyright (c) 2001 Gregory P. Ward. All rights reserved.
12 # See the README.txt distributed with Optik for licensing terms.
14 # created 2001/10/17, GPW (from optik.py)
16 import sys, os
17 import types
18 from Cheetah.Utils.optik.option import Option, NO_DEFAULT
19 from Cheetah.Utils.optik.errors import OptionConflictError, OptionValueError, BadOptionError
21 def get_prog_name ():
22 return os.path.basename(sys.argv[0])
25 SUPPRESS_HELP = "SUPPRESS"+"HELP"
26 SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
28 STD_HELP_OPTION = Option("-h", "--help",
29 action="help",
30 help="show this help message and exit")
31 STD_VERSION_OPTION = Option("--version",
32 action="version",
33 help="show program's version number and exit")
36 class Values:
38 def __init__ (self, defaults=None):
39 if defaults:
40 for (attr, val) in defaults.items():
41 setattr(self, attr, val)
44 def _update_careful (self, dict):
45 """
46 Update the option values from an arbitrary dictionary, but only
47 use keys from dict that already have a corresponding attribute
48 in self. Any keys in dict without a corresponding attribute
49 are silently ignored.
50 """
51 for attr in dir(self):
52 if dict.has_key(attr):
53 dval = dict[attr]
54 if dval is not None:
55 setattr(self, attr, dval)
57 def _update_loose (self, dict):
58 """
59 Update the option values from an arbitrary dictionary,
60 using all keys from the dictionary regardless of whether
61 they have a corresponding attribute in self or not.
62 """
63 self.__dict__.update(dict)
65 def _update (self, dict, mode):
66 if mode == "careful":
67 self._update_careful(dict)
68 elif mode == "loose":
69 self._update_loose(dict)
70 else:
71 raise ValueError, "invalid update mode: %r" % mode
73 def read_module (self, modname, mode="careful"):
74 __import__(modname)
75 mod = sys.modules[modname]
76 self._update(vars(mod), mode)
78 def read_file (self, filename, mode="careful"):
79 vars = {}
80 execfile(filename, vars)
81 self._update(vars, mode)
83 def ensure_value (self, attr, value):
84 if not hasattr(self, attr) or getattr(self, attr) is None:
85 setattr(self, attr, value)
86 return getattr(self, attr)
89 class OptionParser:
90 """
91 Class attributes:
92 standard_option_list : [Option]
93 list of standard options that will be accepted by all instances
94 of this parser class (intended to be overridden by subclasses).
96 Instance attributes:
97 usage : string
98 a usage string for your program. Before it is displayed
99 to the user, "%prog" will be expanded to the name of
100 your program (os.path.basename(sys.argv[0])).
101 option_list : [Option]
102 the list of all options accepted on the command-line of
103 this program
104 _short_opt : { string : Option }
105 dictionary mapping short option strings, eg. "-f" or "-X",
106 to the Option instances that implement them. If an Option
107 has multiple short option strings, it will appears in this
108 dictionary multiple times.
109 _long_opt : { string : Option }
110 dictionary mapping long option strings, eg. "--file" or
111 "--exclude", to the Option instances that implement them.
112 Again, a given Option can occur multiple times in this
113 dictionary.
114 _long_opts : [string]
115 list of long option strings recognized by this option
116 parser. Should be equal to _long_opt.values().
117 defaults : { string : any }
118 dictionary mapping option destination names to default
119 values for each destination.
121 allow_interspersed_args : boolean = true
122 if true, positional arguments may be interspersed with options.
123 Assuming -a and -b each take a single argument, the command-line
124 -ablah foo bar -bboo baz
125 will be interpreted the same as
126 -ablah -bboo -- foo bar baz
127 If this flag were false, that command line would be interpreted as
128 -ablah -- foo bar -bboo baz
129 -- ie. we stop processing options as soon as we see the first
130 non-option argument. (This is the tradition followed by
131 Python's getopt module, Perl's Getopt::Std, and other argument-
132 parsing libraries, but it is generally annoying to users.)
134 rargs : [string]
135 the argument list currently being parsed. Only set when
136 parse_args() is active, and continually trimmed down as
137 we consume arguments. Mainly there for the benefit of
138 callback options.
139 largs : [string]
140 the list of leftover arguments that we have skipped while
141 parsing options. If allow_interspersed_args is false, this
142 list is always empty.
143 values : Values
144 the set of option values currently being accumulated. Only
145 set when parse_args() is active. Also mainly for callbacks.
147 Because of the 'rargs', 'largs', and 'values' attributes,
148 OptionParser is not thread-safe. If, for some perverse reason, you
149 need to parse command-line arguments simultaneously in different
150 threads, use different OptionParser instances.
152 """
154 standard_option_list = [STD_HELP_OPTION]
157 def __init__ (self,
158 usage=None,
159 option_list=None,
160 option_class=Option,
161 version=None,
162 conflict_handler="error"):
163 self.set_usage(usage)
164 self.option_class = option_class
165 self.version = version
166 self.set_conflict_handler(conflict_handler)
167 self.allow_interspersed_args = 1
169 # Create the various lists and dicts that constitute the
170 # "option list". See class docstring for details about
171 # each attribute.
172 self._create_option_list()
174 # Populate the option list; initial sources are the
175 # standard_option_list class attribute, the 'option_list'
176 # argument, and the STD_VERSION_OPTION global (if 'version'
177 # supplied).
178 self._populate_option_list(option_list)
180 self._init_parsing_state()
182 # -- Private methods -----------------------------------------------
183 # (used by the constructor)
185 def _create_option_list (self):
186 self.option_list = []
187 self._short_opt = {} # single letter -> Option instance
188 self._long_opt = {} # long option -> Option instance
189 self._long_opts = [] # list of long options
190 self.defaults = {} # maps option dest -> default value
192 def _populate_option_list (self, option_list):
193 if self.standard_option_list:
194 self.add_options(self.standard_option_list)
195 if self.version:
196 self.add_option(STD_VERSION_OPTION)
197 if option_list:
198 self.add_options(option_list)
200 def _init_parsing_state (self):
201 # These are set in parse_args() for the convenience of callbacks.
202 self.rargs = None
203 self.largs = None
204 self.values = None
207 # -- Simple modifier methods ---------------------------------------
209 def set_usage (self, usage):
210 if usage is None:
211 self.usage = "usage: %prog [options]"
212 elif usage is SUPPRESS_USAGE:
213 self.usage = None
214 else:
215 self.usage = usage
217 def enable_interspersed_args (self):
218 self.allow_interspersed_args = 1
220 def disable_interspersed_args (self):
221 self.allow_interspersed_args = 0
223 def set_conflict_handler (self, handler):
224 if handler not in ("ignore", "error", "resolve"):
225 raise ValueError, "invalid conflict_resolution value %r" % handler
226 self.conflict_handler = handler
228 def set_default (self, dest, value):
229 self.defaults[dest] = value
231 def set_defaults (self, **kwargs):
232 self.defaults.update(kwargs)
235 # -- Option-adding methods -----------------------------------------
237 def _check_conflict (self, option):
238 conflict_opts = []
239 for opt in option._short_opts:
240 if self._short_opt.has_key(opt):
241 conflict_opts.append((opt, self._short_opt[opt]))
242 for opt in option._long_opts:
243 if self._long_opt.has_key(opt):
244 conflict_opts.append((opt, self._long_opt[opt]))
246 if conflict_opts:
247 handler = self.conflict_handler
248 if handler == "ignore": # behaviour for Optik 1.0, 1.1
249 pass
250 elif handler == "error": # new in 1.2
251 raise OptionConflictError(
252 "conflicting option string(s): %s"
253 % ", ".join([co[0] for co in conflict_opts]),
254 option)
255 elif handler == "resolve": # new in 1.2
256 for (opt, c_option) in conflict_opts:
257 if opt.startswith("--"):
258 c_option._long_opts.remove(opt)
259 del self._long_opt[opt]
260 else:
261 c_option._short_opts.remove(opt)
262 del self._short_opt[opt]
263 if not (c_option._short_opts or c_option._long_opts):
264 self.option_list.remove(c_option)
267 def add_option (self, *args, **kwargs):
268 """add_option(Option)
269 add_option(opt_str, ..., kwarg=val, ...)
271 if type(args[0]) is types.StringType:
272 option = self.option_class(*args, **kwargs)
273 elif len(args) == 1 and not kwargs:
274 option = args[0]
275 if not isinstance(option, Option):
276 raise TypeError, "not an Option instance: %r" % option
277 else:
278 raise TypeError, "invalid arguments"
280 self._check_conflict(option)
282 self.option_list.append(option)
283 for opt in option._short_opts:
284 self._short_opt[opt] = option
285 for opt in option._long_opts:
286 self._long_opt[opt] = option
287 self._long_opts.append(opt)
289 if option.dest is not None: # option has a dest, we need a default
290 if option.default is not NO_DEFAULT:
291 self.defaults[option.dest] = option.default
292 elif not self.defaults.has_key(option.dest):
293 self.defaults[option.dest] = None
295 def add_options (self, option_list):
296 for option in option_list:
297 self.add_option(option)
300 # -- Option query/removal methods ----------------------------------
302 def get_option (self, opt_str):
303 return (self._short_opt.get(opt_str) or
304 self._long_opt.get(opt_str))
306 def has_option (self, opt_str):
307 return (self._short_opt.has_key(opt_str) or
308 self._long_opt.has_key(opt_str))
311 def remove_option (self, opt_str):
312 option = self._short_opt.get(opt_str)
313 if option is None:
314 option = self._long_opt.get(opt_str)
315 if option is None:
316 raise ValueError("no such option %r" % opt_str)
318 for opt in option._short_opts:
319 del self._short_opt[opt]
320 for opt in option._long_opts:
321 del self._long_opt[opt]
322 self._long_opts.remove(opt)
323 self.option_list.remove(option)
326 # -- Option-parsing methods ----------------------------------------
328 def _get_args (self, args):
329 if args is None:
330 return sys.argv[1:]
331 else:
332 return args[:] # don't modify caller's list
334 def parse_args (self, args=None, values=None):
336 parse_args(args : [string] = sys.argv[1:],
337 values : Values = None)
338 -> (values : Values, args : [string])
340 Parse the command-line options found in 'args' (default:
341 sys.argv[1:]). Any errors result in a call to 'error()', which
342 by default prints the usage message to stderr and calls
343 sys.exit() with an error message. On success returns a pair
344 (values, args) where 'values' is an Values instance (with all
345 your option values) and 'args' is the list of arguments left
346 over after parsing options.
348 rargs = self._get_args(args)
349 if values is None:
350 values = Values(self.defaults)
352 # Store the halves of the argument list as attributes for the
353 # convenience of callbacks:
354 # rargs
355 # the rest of the command-line (the "r" stands for
356 # "remaining" or "right-hand")
357 # largs
358 # the leftover arguments -- ie. what's left after removing
359 # options and their arguments (the "l" stands for "leftover"
360 # or "left-hand")
362 # Say this is the original argument list:
363 # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
365 # (we are about to process arg(i)).
367 # Then rargs is [arg(i), ..., arg(N-1)]
368 # and largs is a *subset* of [arg0, ..., arg(i-1)]
369 # (any options and their arguments will have been removed
370 # from largs).
372 # _process_arg() will always consume 1 or more arguments.
373 # If it consumes 1 (eg. arg is an option that takes no arguments),
374 # then after _process_arg() is done the situation is:
375 # largs = subset of [arg0, ..., arg(i)]
376 # rargs = [arg(i+1), ..., arg(N-1)]
378 # If allow_interspersed_args is false, largs will always be
379 # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
380 # not a very interesting subset!
382 self.rargs = rargs
383 self.largs = largs = []
384 self.values = values
386 stop = 0
387 while rargs and not stop:
388 try:
389 stop = self._process_arg(largs, rargs, values)
390 except (BadOptionError, OptionValueError), err:
391 self.error(err.msg)
393 args = largs + rargs
394 return self.check_values(values, args)
396 def check_values (self, values, args):
398 check_values(values : Values, args : [string])
399 -> (values : Values, args : [string])
401 Check that the supplied option values and leftover arguments are
402 valid. Returns the option values and leftover arguments
403 (possibly adjusted, possibly completely new -- whatever you
404 like). Default implementation just returns the passed-in
405 values; subclasses may override as desired.
407 return (values, args)
409 def _process_arg (self, largs, rargs, values):
410 """_process_args(largs : [string],
411 rargs : [string],
412 values : Values)
413 -> stop : boolean
415 Process a single command-line argument, consuming zero or more
416 arguments. The next argument to process is rargs[0], which will
417 almost certainly be consumed from rargs. (It might wind up in
418 largs, or it might affect a value in values, or -- if a callback
419 is involved -- almost anything might happen. It will not be
420 consumed if it is a non-option argument and
421 allow_interspersed_args is false.) More arguments from rargs
422 may also be consumed, depending on circumstances.
424 Returns true if option processing should stop after this
425 argument is processed.
428 # We handle bare "--" explicitly, and bare "-" is handled by the
429 # standard arg handler since the short arg case ensures that the len
430 # of the opt string is greater than 1.
432 arg = rargs[0]
433 if arg == "--":
434 del rargs[0]
435 return 1
436 elif arg[0:2] == "--":
437 # process a single long option (possibly with value(s))
438 self._process_long_opt(rargs, values)
439 elif arg[:1] == "-" and len(arg) > 1:
440 # process a cluster of short options (possibly with
441 # value(s) for the last one only)
442 self._process_short_opts(rargs, values)
443 else:
444 if self.allow_interspersed_args:
445 largs.append(arg)
446 del rargs[0]
447 else:
448 return 1 # stop now, leave this arg in rargs
450 return 0 # keep processing args
452 def _match_long_opt (self, opt):
453 """_match_long_opt(opt : string) -> string
455 Determine which long option string 'opt' matches, ie. which one
456 it is an unambiguous abbrevation for. Raises BadOptionError if
457 'opt' doesn't unambiguously match any long option string.
459 return _match_abbrev(opt, self._long_opts)
461 def _process_long_opt (self, rargs, values):
462 arg = rargs.pop(0)
464 # Value explicitly attached to arg? Pretend it's the next
465 # argument.
466 if "=" in arg:
467 (opt, next_arg) = arg.split("=", 1)
468 rargs.insert(0, next_arg)
469 had_explicit_value = 1
470 else:
471 opt = arg
472 had_explicit_value = 0
474 opt = self._match_long_opt(opt)
475 option = self._long_opt[opt]
476 if option.takes_value():
477 nargs = option.nargs
478 if len(rargs) < nargs:
479 if nargs == 1:
480 self.error("%s option requires a value" % opt)
481 else:
482 self.error("%s option requires %d values"
483 % (opt, nargs))
484 elif nargs == 1:
485 value = rargs.pop(0)
486 else:
487 value = tuple(rargs[0:nargs])
488 del rargs[0:nargs]
490 elif had_explicit_value:
491 self.error("%s option does not take a value" % opt)
493 else:
494 value = None
496 option.process(opt, value, values, self)
498 def _process_short_opts (self, rargs, values):
499 arg = rargs.pop(0)
500 stop = 0
501 i = 1
502 for ch in arg[1:]:
503 opt = "-" + ch
504 option = self._short_opt.get(opt)
505 i += 1 # we have consumed a character
507 if not option:
508 self.error("no such option: %s" % opt)
509 if option.takes_value():
510 # Any characters left in arg? Pretend they're the
511 # next arg, and stop consuming characters of arg.
512 if i < len(arg):
513 rargs.insert(0, arg[i:])
514 stop = 1
516 nargs = option.nargs
517 if len(rargs) < nargs:
518 if nargs == 1:
519 self.error("%s option requires a value" % opt)
520 else:
521 self.error("%s option requires %s values"
522 % (opt, nargs))
523 elif nargs == 1:
524 value = rargs.pop(0)
525 else:
526 value = tuple(rargs[0:nargs])
527 del rargs[0:nargs]
529 else: # option doesn't take a value
530 value = None
532 option.process(opt, value, values, self)
534 if stop:
535 break
538 # -- Output/error methods ------------------------------------------
540 def error (self, msg):
541 self.print_usage(sys.stderr)
542 sys.exit("%s: error: %s" % (get_prog_name(), msg))
544 def print_usage (self, file=None):
545 if self.usage:
546 usage = self.usage.replace("%prog", get_prog_name())
547 print >>file, usage
548 print >>file
550 def print_version (self, file=None):
551 if self.version:
552 version = self.version.replace("%prog", get_prog_name())
553 print >>file, version
555 def print_help (self, file=None):
556 from distutils.fancy_getopt import wrap_text
558 if file is None:
559 file = sys.stdout
561 self.print_usage(file)
563 # The help for each option consists of two parts:
564 # * the opt strings and metavars
565 # eg. ("-x", or "-fFILENAME, --file=FILENAME")
566 # * the user-supplied help string
567 # eg. ("turn on expert mode", "read data from FILENAME")
569 # If possible, we write both of these on the same line:
570 # -x turn on expert mode
572 # But if the opt string list is too long, we put the help
573 # string on a second line, indented to the same column it would
574 # start in if it fit on the first line.
575 # -fFILENAME, --file=FILENAME
576 # read data from FILENAME
578 print >>file, "options:"
579 width = 78 # assume 80 cols for now
581 option_help = [] # list of (string, string) tuples
582 lengths = []
584 for option in self.option_list:
585 takes_value = option.takes_value()
586 if takes_value:
587 metavar = option.metavar or option.dest.upper()
589 opts = [] # list of "-a" or "--foo=FILE" strings
590 if option.help is SUPPRESS_HELP:
591 continue
593 if takes_value:
594 for sopt in option._short_opts:
595 opts.append(sopt + metavar)
596 for lopt in option._long_opts:
597 opts.append(lopt + "=" + metavar)
598 else:
599 for opt in option._short_opts + option._long_opts:
600 opts.append(opt)
602 opts = ", ".join(opts)
603 option_help.append((opts, option.help))
604 lengths.append(len(opts))
606 max_opts = min(max(lengths), 20)
608 for (opts, help) in option_help:
609 # how much to indent lines 2 .. N of help text
610 indent_rest = 2 + max_opts + 2
611 help_width = width - indent_rest
613 if len(opts) > max_opts:
614 opts = " " + opts + "\n"
615 indent_first = indent_rest
617 else: # start help on same line as opts
618 opts = " %-*s " % (max_opts, opts)
619 indent_first = 0
621 file.write(opts)
623 if help:
624 help_lines = wrap_text(help, help_width)
625 print >>file, "%*s%s" % (indent_first, "", help_lines[0])
626 for line in help_lines[1:]:
627 print >>file, "%*s%s" % (indent_rest, "", line)
628 elif opts[-1] != "\n":
629 file.write("\n")
631 # class OptionParser
634 def _match_abbrev (s, words):
635 """_match_abbrev(s : string, words : [string]) -> string
637 Returns the string in 'words' for which 's' is an unambiguous
638 abbreviation. If 's' is found to be ambiguous or doesn't match any
639 of 'words', raises BadOptionError.
640 """
641 match = None
642 for word in words:
643 # If s isn't even a prefix for this word, don't waste any
644 # more time on it: skip to the next word and try again.
645 if not word.startswith(s):
646 continue
648 # Exact match? Great, return now.
649 if s == word:
650 return word
652 # Now comes the tricky business of disambiguation. At this
653 # point, we know s is a proper prefix of word, eg. s='--foo' and
654 # word=='--foobar'. If we have already seen another word where
655 # this was the case, eg. '--foobaz', fail: s is ambiguous.
656 # Otherwise record this match and keep looping; we will return
657 # if we see an exact match, or when we fall out of the loop and
658 # it turns out that the current word is the match.
659 if match:
660 raise BadOptionError("ambiguous option: %s (%s, %s, ...?)"
661 % (s, match, word))
662 match = word
664 if match:
665 return match
666 else:
667 raise BadOptionError("no such option: %s" % s)