| | |
| | """ |
| | This module started out as largely a copy paste from the stdlib's |
| | optparse module with the features removed that we do not need from |
| | optparse because we implement them in Click on a higher level (for |
| | instance type handling, help formatting and a lot more). |
| | |
| | The plan is to remove more and more from here over time. |
| | |
| | The reason this is a different module and not optparse from the stdlib |
| | is that there are differences in 2.x and 3.x about the error messages |
| | generated and optparse in the stdlib uses gettext for no good reason |
| | and might cause us issues. |
| | |
| | Click uses parts of optparse written by Gregory P. Ward and maintained |
| | by the Python Software Foundation. This is limited to code in parser.py. |
| | |
| | Copyright 2001-2006 Gregory P. Ward. All rights reserved. |
| | Copyright 2002-2006 Python Software Foundation. All rights reserved. |
| | """ |
| | import re |
| | from collections import deque |
| |
|
| | from .exceptions import BadArgumentUsage |
| | from .exceptions import BadOptionUsage |
| | from .exceptions import NoSuchOption |
| | from .exceptions import UsageError |
| |
|
| |
|
| | def _unpack_args(args, nargs_spec): |
| | """Given an iterable of arguments and an iterable of nargs specifications, |
| | it returns a tuple with all the unpacked arguments at the first index |
| | and all remaining arguments as the second. |
| | |
| | The nargs specification is the number of arguments that should be consumed |
| | or `-1` to indicate that this position should eat up all the remainders. |
| | |
| | Missing items are filled with `None`. |
| | """ |
| | args = deque(args) |
| | nargs_spec = deque(nargs_spec) |
| | rv = [] |
| | spos = None |
| |
|
| | def _fetch(c): |
| | try: |
| | if spos is None: |
| | return c.popleft() |
| | else: |
| | return c.pop() |
| | except IndexError: |
| | return None |
| |
|
| | while nargs_spec: |
| | nargs = _fetch(nargs_spec) |
| | if nargs == 1: |
| | rv.append(_fetch(args)) |
| | elif nargs > 1: |
| | x = [_fetch(args) for _ in range(nargs)] |
| | |
| | |
| | if spos is not None: |
| | x.reverse() |
| | rv.append(tuple(x)) |
| | elif nargs < 0: |
| | if spos is not None: |
| | raise TypeError("Cannot have two nargs < 0") |
| | spos = len(rv) |
| | rv.append(None) |
| |
|
| | |
| | |
| | if spos is not None: |
| | rv[spos] = tuple(args) |
| | args = [] |
| | rv[spos + 1 :] = reversed(rv[spos + 1 :]) |
| |
|
| | return tuple(rv), list(args) |
| |
|
| |
|
| | def _error_opt_args(nargs, opt): |
| | if nargs == 1: |
| | raise BadOptionUsage(opt, "{} option requires an argument".format(opt)) |
| | raise BadOptionUsage(opt, "{} option requires {} arguments".format(opt, nargs)) |
| |
|
| |
|
| | def split_opt(opt): |
| | first = opt[:1] |
| | if first.isalnum(): |
| | return "", opt |
| | if opt[1:2] == first: |
| | return opt[:2], opt[2:] |
| | return first, opt[1:] |
| |
|
| |
|
| | def normalize_opt(opt, ctx): |
| | if ctx is None or ctx.token_normalize_func is None: |
| | return opt |
| | prefix, opt = split_opt(opt) |
| | return prefix + ctx.token_normalize_func(opt) |
| |
|
| |
|
| | def split_arg_string(string): |
| | """Given an argument string this attempts to split it into small parts.""" |
| | rv = [] |
| | for match in re.finditer( |
| | r"('([^'\\]*(?:\\.[^'\\]*)*)'|\"([^\"\\]*(?:\\.[^\"\\]*)*)\"|\S+)\s*", |
| | string, |
| | re.S, |
| | ): |
| | arg = match.group().strip() |
| | if arg[:1] == arg[-1:] and arg[:1] in "\"'": |
| | arg = arg[1:-1].encode("ascii", "backslashreplace").decode("unicode-escape") |
| | try: |
| | arg = type(string)(arg) |
| | except UnicodeError: |
| | pass |
| | rv.append(arg) |
| | return rv |
| |
|
| |
|
| | class Option(object): |
| | def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): |
| | self._short_opts = [] |
| | self._long_opts = [] |
| | self.prefixes = set() |
| |
|
| | for opt in opts: |
| | prefix, value = split_opt(opt) |
| | if not prefix: |
| | raise ValueError("Invalid start character for option ({})".format(opt)) |
| | self.prefixes.add(prefix[0]) |
| | if len(prefix) == 1 and len(value) == 1: |
| | self._short_opts.append(opt) |
| | else: |
| | self._long_opts.append(opt) |
| | self.prefixes.add(prefix) |
| |
|
| | if action is None: |
| | action = "store" |
| |
|
| | self.dest = dest |
| | self.action = action |
| | self.nargs = nargs |
| | self.const = const |
| | self.obj = obj |
| |
|
| | @property |
| | def takes_value(self): |
| | return self.action in ("store", "append") |
| |
|
| | def process(self, value, state): |
| | if self.action == "store": |
| | state.opts[self.dest] = value |
| | elif self.action == "store_const": |
| | state.opts[self.dest] = self.const |
| | elif self.action == "append": |
| | state.opts.setdefault(self.dest, []).append(value) |
| | elif self.action == "append_const": |
| | state.opts.setdefault(self.dest, []).append(self.const) |
| | elif self.action == "count": |
| | state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 |
| | else: |
| | raise ValueError("unknown action '{}'".format(self.action)) |
| | state.order.append(self.obj) |
| |
|
| |
|
| | class Argument(object): |
| | def __init__(self, dest, nargs=1, obj=None): |
| | self.dest = dest |
| | self.nargs = nargs |
| | self.obj = obj |
| |
|
| | def process(self, value, state): |
| | if self.nargs > 1: |
| | holes = sum(1 for x in value if x is None) |
| | if holes == len(value): |
| | value = None |
| | elif holes != 0: |
| | raise BadArgumentUsage( |
| | "argument {} takes {} values".format(self.dest, self.nargs) |
| | ) |
| | state.opts[self.dest] = value |
| | state.order.append(self.obj) |
| |
|
| |
|
| | class ParsingState(object): |
| | def __init__(self, rargs): |
| | self.opts = {} |
| | self.largs = [] |
| | self.rargs = rargs |
| | self.order = [] |
| |
|
| |
|
| | class OptionParser(object): |
| | """The option parser is an internal class that is ultimately used to |
| | parse options and arguments. It's modelled after optparse and brings |
| | a similar but vastly simplified API. It should generally not be used |
| | directly as the high level Click classes wrap it for you. |
| | |
| | It's not nearly as extensible as optparse or argparse as it does not |
| | implement features that are implemented on a higher level (such as |
| | types or defaults). |
| | |
| | :param ctx: optionally the :class:`~click.Context` where this parser |
| | should go with. |
| | """ |
| |
|
| | def __init__(self, ctx=None): |
| | |
| | |
| | self.ctx = ctx |
| | |
| | |
| | |
| | |
| | self.allow_interspersed_args = True |
| | |
| | |
| | |
| | |
| | self.ignore_unknown_options = False |
| | if ctx is not None: |
| | self.allow_interspersed_args = ctx.allow_interspersed_args |
| | self.ignore_unknown_options = ctx.ignore_unknown_options |
| | self._short_opt = {} |
| | self._long_opt = {} |
| | self._opt_prefixes = {"-", "--"} |
| | self._args = [] |
| |
|
| | def add_option(self, opts, dest, action=None, nargs=1, const=None, obj=None): |
| | """Adds a new option named `dest` to the parser. The destination |
| | is not inferred (unlike with optparse) and needs to be explicitly |
| | provided. Action can be any of ``store``, ``store_const``, |
| | ``append``, ``appnd_const`` or ``count``. |
| | |
| | The `obj` can be used to identify the option in the order list |
| | that is returned from the parser. |
| | """ |
| | if obj is None: |
| | obj = dest |
| | opts = [normalize_opt(opt, self.ctx) for opt in opts] |
| | option = Option(opts, dest, action=action, nargs=nargs, const=const, obj=obj) |
| | self._opt_prefixes.update(option.prefixes) |
| | for opt in option._short_opts: |
| | self._short_opt[opt] = option |
| | for opt in option._long_opts: |
| | self._long_opt[opt] = option |
| |
|
| | def add_argument(self, dest, nargs=1, obj=None): |
| | """Adds a positional argument named `dest` to the parser. |
| | |
| | The `obj` can be used to identify the option in the order list |
| | that is returned from the parser. |
| | """ |
| | if obj is None: |
| | obj = dest |
| | self._args.append(Argument(dest=dest, nargs=nargs, obj=obj)) |
| |
|
| | def parse_args(self, args): |
| | """Parses positional arguments and returns ``(values, args, order)`` |
| | for the parsed options and arguments as well as the leftover |
| | arguments if there are any. The order is a list of objects as they |
| | appear on the command line. If arguments appear multiple times they |
| | will be memorized multiple times as well. |
| | """ |
| | state = ParsingState(args) |
| | try: |
| | self._process_args_for_options(state) |
| | self._process_args_for_args(state) |
| | except UsageError: |
| | if self.ctx is None or not self.ctx.resilient_parsing: |
| | raise |
| | return state.opts, state.largs, state.order |
| |
|
| | def _process_args_for_args(self, state): |
| | pargs, args = _unpack_args( |
| | state.largs + state.rargs, [x.nargs for x in self._args] |
| | ) |
| |
|
| | for idx, arg in enumerate(self._args): |
| | arg.process(pargs[idx], state) |
| |
|
| | state.largs = args |
| | state.rargs = [] |
| |
|
| | def _process_args_for_options(self, state): |
| | while state.rargs: |
| | arg = state.rargs.pop(0) |
| | arglen = len(arg) |
| | |
| | |
| | if arg == "--": |
| | return |
| | elif arg[:1] in self._opt_prefixes and arglen > 1: |
| | self._process_opts(arg, state) |
| | elif self.allow_interspersed_args: |
| | state.largs.append(arg) |
| | else: |
| | state.rargs.insert(0, arg) |
| | return |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | def _match_long_opt(self, opt, explicit_value, state): |
| | if opt not in self._long_opt: |
| | possibilities = [word for word in self._long_opt if word.startswith(opt)] |
| | raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) |
| |
|
| | option = self._long_opt[opt] |
| | if option.takes_value: |
| | |
| | |
| | |
| | |
| | if explicit_value is not None: |
| | state.rargs.insert(0, explicit_value) |
| |
|
| | nargs = option.nargs |
| | if len(state.rargs) < nargs: |
| | _error_opt_args(nargs, opt) |
| | elif nargs == 1: |
| | value = state.rargs.pop(0) |
| | else: |
| | value = tuple(state.rargs[:nargs]) |
| | del state.rargs[:nargs] |
| |
|
| | elif explicit_value is not None: |
| | raise BadOptionUsage(opt, "{} option does not take a value".format(opt)) |
| |
|
| | else: |
| | value = None |
| |
|
| | option.process(value, state) |
| |
|
| | def _match_short_opt(self, arg, state): |
| | stop = False |
| | i = 1 |
| | prefix = arg[0] |
| | unknown_options = [] |
| |
|
| | for ch in arg[1:]: |
| | opt = normalize_opt(prefix + ch, self.ctx) |
| | option = self._short_opt.get(opt) |
| | i += 1 |
| |
|
| | if not option: |
| | if self.ignore_unknown_options: |
| | unknown_options.append(ch) |
| | continue |
| | raise NoSuchOption(opt, ctx=self.ctx) |
| | if option.takes_value: |
| | |
| | |
| | if i < len(arg): |
| | state.rargs.insert(0, arg[i:]) |
| | stop = True |
| |
|
| | nargs = option.nargs |
| | if len(state.rargs) < nargs: |
| | _error_opt_args(nargs, opt) |
| | elif nargs == 1: |
| | value = state.rargs.pop(0) |
| | else: |
| | value = tuple(state.rargs[:nargs]) |
| | del state.rargs[:nargs] |
| |
|
| | else: |
| | value = None |
| |
|
| | option.process(value, state) |
| |
|
| | if stop: |
| | break |
| |
|
| | |
| | |
| | |
| | |
| | if self.ignore_unknown_options and unknown_options: |
| | state.largs.append("{}{}".format(prefix, "".join(unknown_options))) |
| |
|
| | def _process_opts(self, arg, state): |
| | explicit_value = None |
| | |
| | |
| | |
| | if "=" in arg: |
| | long_opt, explicit_value = arg.split("=", 1) |
| | else: |
| | long_opt = arg |
| | norm_long_opt = normalize_opt(long_opt, self.ctx) |
| |
|
| | |
| | |
| | |
| | try: |
| | self._match_long_opt(norm_long_opt, explicit_value, state) |
| | except NoSuchOption: |
| | |
| | |
| | |
| | |
| | |
| | |
| | if arg[:2] not in self._opt_prefixes: |
| | return self._match_short_opt(arg, state) |
| | if not self.ignore_unknown_options: |
| | raise |
| | state.largs.append(arg) |
| |
|