]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/click/parser.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / click / parser.py
1 """
2 This module started out as largely a copy paste from the stdlib's
3 optparse module with the features removed that we do not need from
4 optparse because we implement them in Click on a higher level (for
5 instance type handling, help formatting and a lot more).
6
7 The plan is to remove more and more from here over time.
8
9 The reason this is a different module and not optparse from the stdlib
10 is that there are differences in 2.x and 3.x about the error messages
11 generated and optparse in the stdlib uses gettext for no good reason
12 and might cause us issues.
13
14 Click uses parts of optparse written by Gregory P. Ward and maintained
15 by the Python Software Foundation. This is limited to code in parser.py.
16
17 Copyright 2001-2006 Gregory P. Ward. All rights reserved.
18 Copyright 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
24 import typing as t
25 from collections import deque
26 from gettext import gettext as _
27 from gettext import ngettext
28
29 from .exceptions import BadArgumentUsage
30 from .exceptions import BadOptionUsage
31 from .exceptions import NoSuchOption
32 from .exceptions import UsageError
33
34 if 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
40
41 V = t.TypeVar("V")
42
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()
47
48
49 def _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.
55
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.
58
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
65
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
74
75 while nargs_spec:
76 nargs = _fetch(nargs_spec)
77
78 if nargs is None:
79 continue
80
81 if nargs == 1:
82 rv.append(_fetch(args))
83 elif nargs > 1:
84 x = [_fetch(args) for _ in range(nargs)]
85
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()
90
91 rv.append(tuple(x))
92 elif nargs < 0:
93 if spos is not None:
94 raise TypeError("Cannot have two nargs < 0")
95
96 spos = len(rv)
97 rv.append(None)
98
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 :])
105
106 return tuple(rv), list(args)
107
108
109 def 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:]
116
117
118 def 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)}"
123
124
125 def 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.
129
130 .. code-block:: python
131
132 split_arg_string("example 'my file")
133 ["example", "my file"]
134
135 split_arg_string("example my\\")
136 ["example", "my"]
137
138 :param string: String to split.
139 """
140 import shlex
141
142 lex = shlex.shlex(string, posix=True)
143 lex.whitespace_split = True
144 lex.commenters = ""
145 out = []
146
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)
155
156 return out
157
158
159 class 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()
172
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)
183
184 if action is None:
185 action = "store"
186
187 self.dest = dest
188 self.action = action
189 self.nargs = nargs
190 self.const = const
191 self.obj = obj
192
193 @property
194 def takes_value(self) -> bool:
195 return self.action in ("store", "append")
196
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)
211
212
213 class 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
218
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 )
235
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
240
241 state.opts[self.dest] = value # type: ignore
242 state.order.append(self.obj)
243
244
245 class 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"] = []
251
252
253 class 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.
258
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).
262
263 :param ctx: optionally the :class:`~click.Context` where this parser
264 should go with.
265 """
266
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
281
282 if ctx is not None:
283 self.allow_interspersed_args = ctx.allow_interspersed_args
284 self.ignore_unknown_options = ctx.ignore_unknown_options
285
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] = []
290
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``.
304
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
315
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.
320
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))
325
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
343
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 )
348
349 for idx, arg in enumerate(self._args):
350 arg.process(pargs[idx], state)
351
352 state.largs = args
353 state.rargs = []
354
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
370
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!
390
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
396
397 possibilities = get_close_matches(opt, self._long_opt)
398 raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
399
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)
408
409 value = self._get_value_from_state(opt, option, state)
410
411 elif explicit_value is not None:
412 raise BadOptionUsage(
413 opt, _("Option {name!r} does not take a value.").format(name=opt)
414 )
415
416 else:
417 value = None
418
419 option.process(value, state)
420
421 def _match_short_opt(self, arg: str, state: ParsingState) -> None:
422 stop = False
423 i = 1
424 prefix = arg[0]
425 unknown_options = []
426
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
431
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
443
444 value = self._get_value_from_state(opt, option, state)
445
446 else:
447 value = None
448
449 option.process(value, state)
450
451 if stop:
452 break
453
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)}")
460
461 def _get_value_from_state(
462 self, option_name: str, option: Option, state: ParsingState
463 ) -> t.Any:
464 nargs = option.nargs
465
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]
481
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]
496
497 return value
498
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)
509
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
525
526 if not self.ignore_unknown_options:
527 raise
528
529 state.largs.append(arg)