| """ |
| 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. |
| """ |
| # This code uses parts of optparse written by Gregory P. Ward and |
| # maintained by the Python Software Foundation. |
| # Copyright 2001-2006 Gregory P. Ward |
| # Copyright 2002-2006 Python Software Foundation |
| import typing as t |
| from collections import deque |
| from gettext import gettext as _ |
| from gettext import ngettext |
| |
| from .exceptions import BadArgumentUsage |
| from .exceptions import BadOptionUsage |
| from .exceptions import NoSuchOption |
| from .exceptions import UsageError |
| |
| if t.TYPE_CHECKING: |
| import typing_extensions as te |
| from .core import Argument as CoreArgument |
| from .core import Context |
| from .core import Option as CoreOption |
| from .core import Parameter as CoreParameter |
| |
| V = t.TypeVar("V") |
| |
| # Sentinel value that indicates an option was passed as a flag without a |
| # value but is not a flag option. Option.consume_value uses this to |
| # prompt or use the flag_value. |
| _flag_needs_value = object() |
| |
| |
| def _unpack_args( |
| args: t.Sequence[str], nargs_spec: t.Sequence[int] |
| ) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: |
| """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: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] |
| spos: t.Optional[int] = None |
| |
| def _fetch(c: "te.Deque[V]") -> t.Optional[V]: |
| 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 is None: |
| continue |
| |
| if nargs == 1: |
| rv.append(_fetch(args)) |
| elif nargs > 1: |
| x = [_fetch(args) for _ in range(nargs)] |
| |
| # If we're reversed, we're pulling in the arguments in reverse, |
| # so we need to turn them around. |
| 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) |
| |
| # spos is the position of the wildcard (star). If it's not `None`, |
| # we fill it with the remainder. |
| if spos is not None: |
| rv[spos] = tuple(args) |
| args = [] |
| rv[spos + 1 :] = reversed(rv[spos + 1 :]) |
| |
| return tuple(rv), list(args) |
| |
| |
| def split_opt(opt: str) -> t.Tuple[str, str]: |
| 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: str, ctx: t.Optional["Context"]) -> str: |
| if ctx is None or ctx.token_normalize_func is None: |
| return opt |
| prefix, opt = split_opt(opt) |
| return f"{prefix}{ctx.token_normalize_func(opt)}" |
| |
| |
| def split_arg_string(string: str) -> t.List[str]: |
| """Split an argument string as with :func:`shlex.split`, but don't |
| fail if the string is incomplete. Ignores a missing closing quote or |
| incomplete escape sequence and uses the partial token as-is. |
| |
| .. code-block:: python |
| |
| split_arg_string("example 'my file") |
| ["example", "my file"] |
| |
| split_arg_string("example my\\") |
| ["example", "my"] |
| |
| :param string: String to split. |
| """ |
| import shlex |
| |
| lex = shlex.shlex(string, posix=True) |
| lex.whitespace_split = True |
| lex.commenters = "" |
| out = [] |
| |
| try: |
| for token in lex: |
| out.append(token) |
| except ValueError: |
| # Raised when end-of-string is reached in an invalid state. Use |
| # the partial token as-is. The quote or escape character is in |
| # lex.state, not lex.token. |
| out.append(lex.token) |
| |
| return out |
| |
| |
| class Option: |
| def __init__( |
| self, |
| obj: "CoreOption", |
| opts: t.Sequence[str], |
| dest: t.Optional[str], |
| action: t.Optional[str] = None, |
| nargs: int = 1, |
| const: t.Optional[t.Any] = None, |
| ): |
| self._short_opts = [] |
| self._long_opts = [] |
| self.prefixes = set() |
| |
| for opt in opts: |
| prefix, value = split_opt(opt) |
| if not prefix: |
| raise ValueError(f"Invalid start character for option ({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) -> bool: |
| return self.action in ("store", "append") |
| |
| def process(self, value: str, state: "ParsingState") -> None: |
| if self.action == "store": |
| state.opts[self.dest] = value # type: ignore |
| elif self.action == "store_const": |
| state.opts[self.dest] = self.const # type: ignore |
| elif self.action == "append": |
| state.opts.setdefault(self.dest, []).append(value) # type: ignore |
| elif self.action == "append_const": |
| state.opts.setdefault(self.dest, []).append(self.const) # type: ignore |
| elif self.action == "count": |
| state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore |
| else: |
| raise ValueError(f"unknown action '{self.action}'") |
| state.order.append(self.obj) |
| |
| |
| class Argument: |
| def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): |
| self.dest = dest |
| self.nargs = nargs |
| self.obj = obj |
| |
| def process( |
| self, |
| value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], |
| state: "ParsingState", |
| ) -> None: |
| if self.nargs > 1: |
| assert value is not None |
| holes = sum(1 for x in value if x is None) |
| if holes == len(value): |
| value = None |
| elif holes != 0: |
| raise BadArgumentUsage( |
| _("Argument {name!r} takes {nargs} values.").format( |
| name=self.dest, nargs=self.nargs |
| ) |
| ) |
| |
| if self.nargs == -1 and self.obj.envvar is not None and value == (): |
| # Replace empty tuple with None so that a value from the |
| # environment may be tried. |
| value = None |
| |
| state.opts[self.dest] = value # type: ignore |
| state.order.append(self.obj) |
| |
| |
| class ParsingState: |
| def __init__(self, rargs: t.List[str]) -> None: |
| self.opts: t.Dict[str, t.Any] = {} |
| self.largs: t.List[str] = [] |
| self.rargs = rargs |
| self.order: t.List["CoreParameter"] = [] |
| |
| |
| class OptionParser: |
| """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: t.Optional["Context"] = None) -> None: |
| #: The :class:`~click.Context` for this parser. This might be |
| #: `None` for some advanced use cases. |
| self.ctx = ctx |
| #: This controls how the parser deals with interspersed arguments. |
| #: If this is set to `False`, the parser will stop on the first |
| #: non-option. Click uses this to implement nested subcommands |
| #: safely. |
| self.allow_interspersed_args = True |
| #: This tells the parser how to deal with unknown options. By |
| #: default it will error out (which is sensible), but there is a |
| #: second mode where it will ignore it and continue processing |
| #: after shifting all the unknown options into the resulting args. |
| 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: t.Dict[str, Option] = {} |
| self._long_opt: t.Dict[str, Option] = {} |
| self._opt_prefixes = {"-", "--"} |
| self._args: t.List[Argument] = [] |
| |
| def add_option( |
| self, |
| obj: "CoreOption", |
| opts: t.Sequence[str], |
| dest: t.Optional[str], |
| action: t.Optional[str] = None, |
| nargs: int = 1, |
| const: t.Optional[t.Any] = None, |
| ) -> 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``, ``append_const`` or ``count``. |
| |
| The `obj` can be used to identify the option in the order list |
| that is returned from the parser. |
| """ |
| opts = [normalize_opt(opt, self.ctx) for opt in opts] |
| option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) |
| 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, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 |
| ) -> 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. |
| """ |
| self._args.append(Argument(obj, dest=dest, nargs=nargs)) |
| |
| def parse_args( |
| self, args: t.List[str] |
| ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: |
| """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: ParsingState) -> None: |
| 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: ParsingState) -> None: |
| while state.rargs: |
| arg = state.rargs.pop(0) |
| arglen = len(arg) |
| # Double dashes always handled explicitly regardless of what |
| # prefixes are valid. |
| 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 |
| |
| # 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). |
| # |
| # The while loop will usually consume 1 or more arguments per pass. |
| # 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! |
| |
| def _match_long_opt( |
| self, opt: str, explicit_value: t.Optional[str], state: ParsingState |
| ) -> None: |
| if opt not in self._long_opt: |
| from difflib import get_close_matches |
| |
| possibilities = get_close_matches(opt, self._long_opt) |
| raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) |
| |
| option = self._long_opt[opt] |
| if option.takes_value: |
| # At this point it's safe to modify rargs by injecting the |
| # explicit value, because no exception is raised in this |
| # branch. This means that the inserted value will be fully |
| # consumed. |
| if explicit_value is not None: |
| state.rargs.insert(0, explicit_value) |
| |
| value = self._get_value_from_state(opt, option, state) |
| |
| elif explicit_value is not None: |
| raise BadOptionUsage( |
| opt, _("Option {name!r} does not take a value.").format(name=opt) |
| ) |
| |
| else: |
| value = None |
| |
| option.process(value, state) |
| |
| def _match_short_opt(self, arg: str, state: ParsingState) -> None: |
| stop = False |
| i = 1 |
| prefix = arg[0] |
| unknown_options = [] |
| |
| for ch in arg[1:]: |
| opt = normalize_opt(f"{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: |
| # Any characters left in arg? Pretend they're the |
| # next arg, and stop consuming characters of arg. |
| if i < len(arg): |
| state.rargs.insert(0, arg[i:]) |
| stop = True |
| |
| value = self._get_value_from_state(opt, option, state) |
| |
| else: |
| value = None |
| |
| option.process(value, state) |
| |
| if stop: |
| break |
| |
| # If we got any unknown options we re-combinate the string of the |
| # remaining options and re-attach the prefix, then report that |
| # to the state as new larg. This way there is basic combinatorics |
| # that can be achieved while still ignoring unknown arguments. |
| if self.ignore_unknown_options and unknown_options: |
| state.largs.append(f"{prefix}{''.join(unknown_options)}") |
| |
| def _get_value_from_state( |
| self, option_name: str, option: Option, state: ParsingState |
| ) -> t.Any: |
| nargs = option.nargs |
| |
| if len(state.rargs) < nargs: |
| if option.obj._flag_needs_value: |
| # Option allows omitting the value. |
| value = _flag_needs_value |
| else: |
| raise BadOptionUsage( |
| option_name, |
| ngettext( |
| "Option {name!r} requires an argument.", |
| "Option {name!r} requires {nargs} arguments.", |
| nargs, |
| ).format(name=option_name, nargs=nargs), |
| ) |
| elif nargs == 1: |
| next_rarg = state.rargs[0] |
| |
| if ( |
| option.obj._flag_needs_value |
| and isinstance(next_rarg, str) |
| and next_rarg[:1] in self._opt_prefixes |
| and len(next_rarg) > 1 |
| ): |
| # The next arg looks like the start of an option, don't |
| # use it as the value if omitting the value is allowed. |
| value = _flag_needs_value |
| else: |
| value = state.rargs.pop(0) |
| else: |
| value = tuple(state.rargs[:nargs]) |
| del state.rargs[:nargs] |
| |
| return value |
| |
| def _process_opts(self, arg: str, state: ParsingState) -> None: |
| explicit_value = None |
| # Long option handling happens in two parts. The first part is |
| # supporting explicitly attached values. In any case, we will try |
| # to long match the option first. |
| if "=" in arg: |
| long_opt, explicit_value = arg.split("=", 1) |
| else: |
| long_opt = arg |
| norm_long_opt = normalize_opt(long_opt, self.ctx) |
| |
| # At this point we will match the (assumed) long option through |
| # the long option matching code. Note that this allows options |
| # like "-foo" to be matched as long options. |
| try: |
| self._match_long_opt(norm_long_opt, explicit_value, state) |
| except NoSuchOption: |
| # At this point the long option matching failed, and we need |
| # to try with short options. However there is a special rule |
| # which says, that if we have a two character options prefix |
| # (applies to "--foo" for instance), we do not dispatch to the |
| # short option code and will instead raise the no option |
| # error. |
| if arg[:2] not in self._opt_prefixes: |
| self._match_short_opt(arg, state) |
| return |
| |
| if not self.ignore_unknown_options: |
| raise |
| |
| state.largs.append(arg) |