📄 option_parser.py
字号:
""" parse_args(args : [string] = sys.argv[1:], values : Values = None) -> (values : Values, args : [string]) Parse the command-line options found in 'args' (default: sys.argv[1:]). Any errors result in a call to 'error()', which by default prints the usage message to stderr and calls sys.exit() with an error message. On success returns a pair (values, args) where 'values' is an Values instance (with all your option values) and 'args' is the list of arguments left over after parsing options. """ rargs = self._get_args(args) if values is None: values = Values(self.defaults) # Store the halves of the argument list as attributes for the # convenience of callbacks: # rargs # the rest of the command-line (the "r" stands for # "remaining" or "right-hand") # largs # the leftover arguments -- ie. what's left after removing # options and their arguments (the "l" stands for "leftover" # or "left-hand") # Say this is the original argument list: # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] # ^ # (we are about to process arg(i)). # # Then rargs is [arg(i), ..., arg(N-1)] # and largs is a *subset* of [arg0, ..., arg(i-1)] # (any options and their arguments will have been removed # from largs). # # _process_arg() will always consume 1 or more arguments. # If it consumes 1 (eg. arg is an option that takes no arguments), # then after _process_arg() is done the situation is: # largs = subset of [arg0, ..., arg(i)] # rargs = [arg(i+1), ..., arg(N-1)] # # If allow_interspersed_args is false, largs will always be # *empty* -- still a subset of [arg0, ..., arg(i-1)], but # not a very interesting subset! self.rargs = rargs self.largs = largs = [] self.values = values stop = 0 while rargs and not stop: try: stop = self._process_arg(largs, rargs, values) except (BadOptionError, OptionValueError), err: self.error(err.msg) args = largs + rargs return self.check_values(values, args) def check_values (self, values, args): """ check_values(values : Values, args : [string]) -> (values : Values, args : [string]) Check that the supplied option values and leftover arguments are valid. Returns the option values and leftover arguments (possibly adjusted, possibly completely new -- whatever you like). Default implementation just returns the passed-in values; subclasses may override as desired. """ return (values, args) def _process_arg (self, largs, rargs, values): """_process_args(largs : [string], rargs : [string], values : Values) -> stop : boolean Process a single command-line argument, consuming zero or more arguments. The next argument to process is rargs[0], which will almost certainly be consumed from rargs. (It might wind up in largs, or it might affect a value in values, or -- if a callback is involved -- almost anything might happen. It will not be consumed if it is a non-option argument and allow_interspersed_args is false.) More arguments from rargs may also be consumed, depending on circumstances. Returns true if option processing should stop after this argument is processed. """ # We handle bare "--" explicitly, and bare "-" is handled by the # standard arg handler since the short arg case ensures that the len # of the opt string is greater than 1. arg = rargs[0] if arg == "--": del rargs[0] return 1 elif arg[0:2] == "--": # process a single long option (possibly with value(s)) self._process_long_opt(rargs, values) elif arg[:1] == "-" and len(arg) > 1: # process a cluster of short options (possibly with # value(s) for the last one only) self._process_short_opts(rargs, values) else: if self.allow_interspersed_args: largs.append(arg) del rargs[0] else: return 1 # stop now, leave this arg in rargs return 0 # keep processing args def _match_long_opt (self, opt): """_match_long_opt(opt : string) -> string Determine which long option string 'opt' matches, ie. which one it is an unambiguous abbrevation for. Raises BadOptionError if 'opt' doesn't unambiguously match any long option string. """ return _match_abbrev(opt, self._long_opts) def _process_long_opt (self, rargs, values): arg = rargs.pop(0) # Value explicitly attached to arg? Pretend it's the next # argument. if "=" in arg: (opt, next_arg) = arg.split("=", 1) rargs.insert(0, next_arg) had_explicit_value = 1 else: opt = arg had_explicit_value = 0 opt = self._match_long_opt(opt) option = self._long_opt[opt] if option.takes_value(): nargs = option.nargs if len(rargs) < nargs: if nargs == 1: self.error("%s option requires a value" % opt) else: self.error("%s option requires %d values" % (opt, nargs)) elif nargs == 1: value = rargs.pop(0) else: value = tuple(rargs[0:nargs]) del rargs[0:nargs] elif had_explicit_value: self.error("%s option does not take a value" % opt) else: value = None option.process(opt, value, values, self) def _process_short_opts (self, rargs, values): arg = rargs.pop(0) stop = 0 i = 1 for ch in arg[1:]: opt = "-" + ch option = self._short_opt.get(opt) i += 1 # we have consumed a character if not option: self.error("no such option: %s" % opt) if option.takes_value(): # Any characters left in arg? Pretend they're the # next arg, and stop consuming characters of arg. if i < len(arg): rargs.insert(0, arg[i:]) stop = 1 nargs = option.nargs if len(rargs) < nargs: if nargs == 1: self.error("%s option requires a value" % opt) else: self.error("%s option requires %s values" % (opt, nargs)) elif nargs == 1: value = rargs.pop(0) else: value = tuple(rargs[0:nargs]) del rargs[0:nargs] else: # option doesn't take a value value = None option.process(opt, value, values, self) if stop: break # -- Output/error methods ------------------------------------------ def error (self, msg): self.print_usage(sys.stderr) sys.exit("%s: error: %s" % (get_prog_name(), msg)) def print_usage (self, file=None): if self.usage: usage = self.usage.replace("%prog", get_prog_name()) print >>file, usage print >>file def print_version (self, file=None): if self.version: version = self.version.replace("%prog", get_prog_name()) print >>file, version def print_help (self, file=None): from distutils.fancy_getopt import wrap_text if file is None: file = sys.stdout self.print_usage(file) # The help for each option consists of two parts: # * the opt strings and metavars # eg. ("-x", or "-fFILENAME, --file=FILENAME") # * the user-supplied help string # eg. ("turn on expert mode", "read data from FILENAME") # # If possible, we write both of these on the same line: # -x turn on expert mode # # But if the opt string list is too long, we put the help # string on a second line, indented to the same column it would # start in if it fit on the first line. # -fFILENAME, --file=FILENAME # read data from FILENAME print >>file, "options:" width = 78 # assume 80 cols for now option_help = [] # list of (string, string) tuples lengths = [] for option in self.option_list: takes_value = option.takes_value() if takes_value: metavar = option.metavar or option.dest.upper() opts = [] # list of "-a" or "--foo=FILE" strings if option.help is SUPPRESS_HELP: continue if takes_value: for sopt in option._short_opts: opts.append(sopt + metavar) for lopt in option._long_opts: opts.append(lopt + "=" + metavar) else: for opt in option._short_opts + option._long_opts: opts.append(opt) opts = ", ".join(opts) option_help.append((opts, option.help)) lengths.append(len(opts)) max_opts = min(max(lengths), 20) for (opts, help) in option_help: # how much to indent lines 2 .. N of help text indent_rest = 2 + max_opts + 2 help_width = width - indent_rest if len(opts) > max_opts: opts = " " + opts + "\n" indent_first = indent_rest else: # start help on same line as opts opts = " %-*s " % (max_opts, opts) indent_first = 0 file.write(opts) if help: help_lines = wrap_text(help, help_width) print >>file, "%*s%s" % (indent_first, "", help_lines[0]) for line in help_lines[1:]: print >>file, "%*s%s" % (indent_rest, "", line) elif opts[-1] != "\n": file.write("\n")# class OptionParserdef _match_abbrev (s, words): """_match_abbrev(s : string, words : [string]) -> string Returns the string in 'words' for which 's' is an unambiguous abbreviation. If 's' is found to be ambiguous or doesn't match any of 'words', raises BadOptionError. """ match = None for word in words: # If s isn't even a prefix for this word, don't waste any # more time on it: skip to the next word and try again. if not word.startswith(s): continue # Exact match? Great, return now. if s == word: return word # Now comes the tricky business of disambiguation. At this # point, we know s is a proper prefix of word, eg. s='--foo' and # word=='--foobar'. If we have already seen another word where # this was the case, eg. '--foobaz', fail: s is ambiguous. # Otherwise record this match and keep looping; we will return # if we see an exact match, or when we fall out of the loop and # it turns out that the current word is the match. if match: raise BadOptionError("ambiguous option: %s (%s, %s, ...?)" % (s, match, word)) match = word if match: return match else: raise BadOptionError("no such option: %s" % s)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -