Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/click/parser.py: 16%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2This module started out as largely a copy paste from the stdlib's
3optparse module with the features removed that we do not need from
4optparse because we implement them in Click on a higher level (for
5instance type handling, help formatting and a lot more).
7The plan is to remove more and more from here over time.
9The reason this is a different module and not optparse from the stdlib
10is that there are differences in 2.x and 3.x about the error messages
11generated and optparse in the stdlib uses gettext for no good reason
12and might cause us issues.
14Click uses parts of optparse written by Gregory P. Ward and maintained
15by the Python Software Foundation. This is limited to code in parser.py.
17Copyright 2001-2006 Gregory P. Ward. All rights reserved.
18Copyright 2002-2006 Python Software Foundation. All rights reserved.
19"""
20# This code uses parts of optparse written by Gregory P. Ward and
21# maintained by the Python Software Foundation.
22# Copyright 2001-2006 Gregory P. Ward
23# Copyright 2002-2006 Python Software Foundation
24import typing as t
25from collections import deque
26from gettext import gettext as _
27from gettext import ngettext
29from .exceptions import BadArgumentUsage
30from .exceptions import BadOptionUsage
31from .exceptions import NoSuchOption
32from .exceptions import UsageError
34if t.TYPE_CHECKING:
35 import typing_extensions as te
36 from .core import Argument as CoreArgument
37 from .core import Context
38 from .core import Option as CoreOption
39 from .core import Parameter as CoreParameter
41V = t.TypeVar("V")
43# Sentinel value that indicates an option was passed as a flag without a
44# value but is not a flag option. Option.consume_value uses this to
45# prompt or use the flag_value.
46_flag_needs_value = object()
49def _unpack_args(
50 args: t.Sequence[str], nargs_spec: t.Sequence[int]
51) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]:
52 """Given an iterable of arguments and an iterable of nargs specifications,
53 it returns a tuple with all the unpacked arguments at the first index
54 and all remaining arguments as the second.
56 The nargs specification is the number of arguments that should be consumed
57 or `-1` to indicate that this position should eat up all the remainders.
59 Missing items are filled with `None`.
60 """
61 args = deque(args)
62 nargs_spec = deque(nargs_spec)
63 rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = []
64 spos: t.Optional[int] = None
66 def _fetch(c: "te.Deque[V]") -> t.Optional[V]:
67 try:
68 if spos is None:
69 return c.popleft()
70 else:
71 return c.pop()
72 except IndexError:
73 return None
75 while nargs_spec:
76 nargs = _fetch(nargs_spec)
78 if nargs is None:
79 continue
81 if nargs == 1:
82 rv.append(_fetch(args))
83 elif nargs > 1:
84 x = [_fetch(args) for _ in range(nargs)]
86 # If we're reversed, we're pulling in the arguments in reverse,
87 # so we need to turn them around.
88 if spos is not None:
89 x.reverse()
91 rv.append(tuple(x))
92 elif nargs < 0:
93 if spos is not None:
94 raise TypeError("Cannot have two nargs < 0")
96 spos = len(rv)
97 rv.append(None)
99 # spos is the position of the wildcard (star). If it's not `None`,
100 # we fill it with the remainder.
101 if spos is not None:
102 rv[spos] = tuple(args)
103 args = []
104 rv[spos + 1 :] = reversed(rv[spos + 1 :])
106 return tuple(rv), list(args)
109def split_opt(opt: str) -> t.Tuple[str, str]:
110 first = opt[:1]
111 if first.isalnum():
112 return "", opt
113 if opt[1:2] == first:
114 return opt[:2], opt[2:]
115 return first, opt[1:]
118def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str:
119 if ctx is None or ctx.token_normalize_func is None:
120 return opt
121 prefix, opt = split_opt(opt)
122 return f"{prefix}{ctx.token_normalize_func(opt)}"
125def split_arg_string(string: str) -> t.List[str]:
126 """Split an argument string as with :func:`shlex.split`, but don't
127 fail if the string is incomplete. Ignores a missing closing quote or
128 incomplete escape sequence and uses the partial token as-is.
130 .. code-block:: python
132 split_arg_string("example 'my file")
133 ["example", "my file"]
135 split_arg_string("example my\\")
136 ["example", "my"]
138 :param string: String to split.
139 """
140 import shlex
142 lex = shlex.shlex(string, posix=True)
143 lex.whitespace_split = True
144 lex.commenters = ""
145 out = []
147 try:
148 for token in lex:
149 out.append(token)
150 except ValueError:
151 # Raised when end-of-string is reached in an invalid state. Use
152 # the partial token as-is. The quote or escape character is in
153 # lex.state, not lex.token.
154 out.append(lex.token)
156 return out
159class Option:
160 def __init__(
161 self,
162 obj: "CoreOption",
163 opts: t.Sequence[str],
164 dest: t.Optional[str],
165 action: t.Optional[str] = None,
166 nargs: int = 1,
167 const: t.Optional[t.Any] = None,
168 ):
169 self._short_opts = []
170 self._long_opts = []
171 self.prefixes: t.Set[str] = set()
173 for opt in opts:
174 prefix, value = split_opt(opt)
175 if not prefix:
176 raise ValueError(f"Invalid start character for option ({opt})")
177 self.prefixes.add(prefix[0])
178 if len(prefix) == 1 and len(value) == 1:
179 self._short_opts.append(opt)
180 else:
181 self._long_opts.append(opt)
182 self.prefixes.add(prefix)
184 if action is None:
185 action = "store"
187 self.dest = dest
188 self.action = action
189 self.nargs = nargs
190 self.const = const
191 self.obj = obj
193 @property
194 def takes_value(self) -> bool:
195 return self.action in ("store", "append")
197 def process(self, value: t.Any, state: "ParsingState") -> None:
198 if self.action == "store":
199 state.opts[self.dest] = value # type: ignore
200 elif self.action == "store_const":
201 state.opts[self.dest] = self.const # type: ignore
202 elif self.action == "append":
203 state.opts.setdefault(self.dest, []).append(value) # type: ignore
204 elif self.action == "append_const":
205 state.opts.setdefault(self.dest, []).append(self.const) # type: ignore
206 elif self.action == "count":
207 state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore
208 else:
209 raise ValueError(f"unknown action '{self.action}'")
210 state.order.append(self.obj)
213class Argument:
214 def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1):
215 self.dest = dest
216 self.nargs = nargs
217 self.obj = obj
219 def process(
220 self,
221 value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]],
222 state: "ParsingState",
223 ) -> None:
224 if self.nargs > 1:
225 assert value is not None
226 holes = sum(1 for x in value if x is None)
227 if holes == len(value):
228 value = None
229 elif holes != 0:
230 raise BadArgumentUsage(
231 _("Argument {name!r} takes {nargs} values.").format(
232 name=self.dest, nargs=self.nargs
233 )
234 )
236 if self.nargs == -1 and self.obj.envvar is not None and value == ():
237 # Replace empty tuple with None so that a value from the
238 # environment may be tried.
239 value = None
241 state.opts[self.dest] = value # type: ignore
242 state.order.append(self.obj)
245class ParsingState:
246 def __init__(self, rargs: t.List[str]) -> None:
247 self.opts: t.Dict[str, t.Any] = {}
248 self.largs: t.List[str] = []
249 self.rargs = rargs
250 self.order: t.List["CoreParameter"] = []
253class OptionParser:
254 """The option parser is an internal class that is ultimately used to
255 parse options and arguments. It's modelled after optparse and brings
256 a similar but vastly simplified API. It should generally not be used
257 directly as the high level Click classes wrap it for you.
259 It's not nearly as extensible as optparse or argparse as it does not
260 implement features that are implemented on a higher level (such as
261 types or defaults).
263 :param ctx: optionally the :class:`~click.Context` where this parser
264 should go with.
265 """
267 def __init__(self, ctx: t.Optional["Context"] = None) -> None:
268 #: The :class:`~click.Context` for this parser. This might be
269 #: `None` for some advanced use cases.
270 self.ctx = ctx
271 #: This controls how the parser deals with interspersed arguments.
272 #: If this is set to `False`, the parser will stop on the first
273 #: non-option. Click uses this to implement nested subcommands
274 #: safely.
275 self.allow_interspersed_args: bool = True
276 #: This tells the parser how to deal with unknown options. By
277 #: default it will error out (which is sensible), but there is a
278 #: second mode where it will ignore it and continue processing
279 #: after shifting all the unknown options into the resulting args.
280 self.ignore_unknown_options: bool = False
282 if ctx is not None:
283 self.allow_interspersed_args = ctx.allow_interspersed_args
284 self.ignore_unknown_options = ctx.ignore_unknown_options
286 self._short_opt: t.Dict[str, Option] = {}
287 self._long_opt: t.Dict[str, Option] = {}
288 self._opt_prefixes = {"-", "--"}
289 self._args: t.List[Argument] = []
291 def add_option(
292 self,
293 obj: "CoreOption",
294 opts: t.Sequence[str],
295 dest: t.Optional[str],
296 action: t.Optional[str] = None,
297 nargs: int = 1,
298 const: t.Optional[t.Any] = None,
299 ) -> None:
300 """Adds a new option named `dest` to the parser. The destination
301 is not inferred (unlike with optparse) and needs to be explicitly
302 provided. Action can be any of ``store``, ``store_const``,
303 ``append``, ``append_const`` or ``count``.
305 The `obj` can be used to identify the option in the order list
306 that is returned from the parser.
307 """
308 opts = [normalize_opt(opt, self.ctx) for opt in opts]
309 option = Option(obj, opts, dest, action=action, nargs=nargs, const=const)
310 self._opt_prefixes.update(option.prefixes)
311 for opt in option._short_opts:
312 self._short_opt[opt] = option
313 for opt in option._long_opts:
314 self._long_opt[opt] = option
316 def add_argument(
317 self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1
318 ) -> None:
319 """Adds a positional argument named `dest` to the parser.
321 The `obj` can be used to identify the option in the order list
322 that is returned from the parser.
323 """
324 self._args.append(Argument(obj, dest=dest, nargs=nargs))
326 def parse_args(
327 self, args: t.List[str]
328 ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]:
329 """Parses positional arguments and returns ``(values, args, order)``
330 for the parsed options and arguments as well as the leftover
331 arguments if there are any. The order is a list of objects as they
332 appear on the command line. If arguments appear multiple times they
333 will be memorized multiple times as well.
334 """
335 state = ParsingState(args)
336 try:
337 self._process_args_for_options(state)
338 self._process_args_for_args(state)
339 except UsageError:
340 if self.ctx is None or not self.ctx.resilient_parsing:
341 raise
342 return state.opts, state.largs, state.order
344 def _process_args_for_args(self, state: ParsingState) -> None:
345 pargs, args = _unpack_args(
346 state.largs + state.rargs, [x.nargs for x in self._args]
347 )
349 for idx, arg in enumerate(self._args):
350 arg.process(pargs[idx], state)
352 state.largs = args
353 state.rargs = []
355 def _process_args_for_options(self, state: ParsingState) -> None:
356 while state.rargs:
357 arg = state.rargs.pop(0)
358 arglen = len(arg)
359 # Double dashes always handled explicitly regardless of what
360 # prefixes are valid.
361 if arg == "--":
362 return
363 elif arg[:1] in self._opt_prefixes and arglen > 1:
364 self._process_opts(arg, state)
365 elif self.allow_interspersed_args:
366 state.largs.append(arg)
367 else:
368 state.rargs.insert(0, arg)
369 return
371 # Say this is the original argument list:
372 # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
373 # ^
374 # (we are about to process arg(i)).
375 #
376 # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
377 # [arg0, ..., arg(i-1)] (any options and their arguments will have
378 # been removed from largs).
379 #
380 # The while loop will usually consume 1 or more arguments per pass.
381 # If it consumes 1 (eg. arg is an option that takes no arguments),
382 # then after _process_arg() is done the situation is:
383 #
384 # largs = subset of [arg0, ..., arg(i)]
385 # rargs = [arg(i+1), ..., arg(N-1)]
386 #
387 # If allow_interspersed_args is false, largs will always be
388 # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
389 # not a very interesting subset!
391 def _match_long_opt(
392 self, opt: str, explicit_value: t.Optional[str], state: ParsingState
393 ) -> None:
394 if opt not in self._long_opt:
395 from difflib import get_close_matches
397 possibilities = get_close_matches(opt, self._long_opt)
398 raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
400 option = self._long_opt[opt]
401 if option.takes_value:
402 # At this point it's safe to modify rargs by injecting the
403 # explicit value, because no exception is raised in this
404 # branch. This means that the inserted value will be fully
405 # consumed.
406 if explicit_value is not None:
407 state.rargs.insert(0, explicit_value)
409 value = self._get_value_from_state(opt, option, state)
411 elif explicit_value is not None:
412 raise BadOptionUsage(
413 opt, _("Option {name!r} does not take a value.").format(name=opt)
414 )
416 else:
417 value = None
419 option.process(value, state)
421 def _match_short_opt(self, arg: str, state: ParsingState) -> None:
422 stop = False
423 i = 1
424 prefix = arg[0]
425 unknown_options = []
427 for ch in arg[1:]:
428 opt = normalize_opt(f"{prefix}{ch}", self.ctx)
429 option = self._short_opt.get(opt)
430 i += 1
432 if not option:
433 if self.ignore_unknown_options:
434 unknown_options.append(ch)
435 continue
436 raise NoSuchOption(opt, ctx=self.ctx)
437 if option.takes_value:
438 # Any characters left in arg? Pretend they're the
439 # next arg, and stop consuming characters of arg.
440 if i < len(arg):
441 state.rargs.insert(0, arg[i:])
442 stop = True
444 value = self._get_value_from_state(opt, option, state)
446 else:
447 value = None
449 option.process(value, state)
451 if stop:
452 break
454 # If we got any unknown options we recombine the string of the
455 # remaining options and re-attach the prefix, then report that
456 # to the state as new larg. This way there is basic combinatorics
457 # that can be achieved while still ignoring unknown arguments.
458 if self.ignore_unknown_options and unknown_options:
459 state.largs.append(f"{prefix}{''.join(unknown_options)}")
461 def _get_value_from_state(
462 self, option_name: str, option: Option, state: ParsingState
463 ) -> t.Any:
464 nargs = option.nargs
466 if len(state.rargs) < nargs:
467 if option.obj._flag_needs_value:
468 # Option allows omitting the value.
469 value = _flag_needs_value
470 else:
471 raise BadOptionUsage(
472 option_name,
473 ngettext(
474 "Option {name!r} requires an argument.",
475 "Option {name!r} requires {nargs} arguments.",
476 nargs,
477 ).format(name=option_name, nargs=nargs),
478 )
479 elif nargs == 1:
480 next_rarg = state.rargs[0]
482 if (
483 option.obj._flag_needs_value
484 and isinstance(next_rarg, str)
485 and next_rarg[:1] in self._opt_prefixes
486 and len(next_rarg) > 1
487 ):
488 # The next arg looks like the start of an option, don't
489 # use it as the value if omitting the value is allowed.
490 value = _flag_needs_value
491 else:
492 value = state.rargs.pop(0)
493 else:
494 value = tuple(state.rargs[:nargs])
495 del state.rargs[:nargs]
497 return value
499 def _process_opts(self, arg: str, state: ParsingState) -> None:
500 explicit_value = None
501 # Long option handling happens in two parts. The first part is
502 # supporting explicitly attached values. In any case, we will try
503 # to long match the option first.
504 if "=" in arg:
505 long_opt, explicit_value = arg.split("=", 1)
506 else:
507 long_opt = arg
508 norm_long_opt = normalize_opt(long_opt, self.ctx)
510 # At this point we will match the (assumed) long option through
511 # the long option matching code. Note that this allows options
512 # like "-foo" to be matched as long options.
513 try:
514 self._match_long_opt(norm_long_opt, explicit_value, state)
515 except NoSuchOption:
516 # At this point the long option matching failed, and we need
517 # to try with short options. However there is a special rule
518 # which says, that if we have a two character options prefix
519 # (applies to "--foo" for instance), we do not dispatch to the
520 # short option code and will instead raise the no option
521 # error.
522 if arg[:2] not in self._opt_prefixes:
523 self._match_short_opt(arg, state)
524 return
526 if not self.ignore_unknown_options:
527 raise
529 state.largs.append(arg)