]> jfr.im git - dlqueue.git/blame - venv/lib/python3.11/site-packages/click/shell_completion.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / click / shell_completion.py
CommitLineData
e0df8241
JR
1import os
2import re
3import typing as t
4from gettext import gettext as _
5
6from .core import Argument
7from .core import BaseCommand
8from .core import Context
9from .core import MultiCommand
10from .core import Option
11from .core import Parameter
12from .core import ParameterSource
13from .parser import split_arg_string
14from .utils import echo
15
16
17def shell_complete(
18 cli: BaseCommand,
19 ctx_args: t.MutableMapping[str, t.Any],
20 prog_name: str,
21 complete_var: str,
22 instruction: str,
23) -> int:
24 """Perform shell completion for the given CLI program.
25
26 :param cli: Command being called.
27 :param ctx_args: Extra arguments to pass to
28 ``cli.make_context``.
29 :param prog_name: Name of the executable in the shell.
30 :param complete_var: Name of the environment variable that holds
31 the completion instruction.
32 :param instruction: Value of ``complete_var`` with the completion
33 instruction and shell, in the form ``instruction_shell``.
34 :return: Status code to exit with.
35 """
36 shell, _, instruction = instruction.partition("_")
37 comp_cls = get_completion_class(shell)
38
39 if comp_cls is None:
40 return 1
41
42 comp = comp_cls(cli, ctx_args, prog_name, complete_var)
43
44 if instruction == "source":
45 echo(comp.source())
46 return 0
47
48 if instruction == "complete":
49 echo(comp.complete())
50 return 0
51
52 return 1
53
54
55class CompletionItem:
56 """Represents a completion value and metadata about the value. The
57 default metadata is ``type`` to indicate special shell handling,
58 and ``help`` if a shell supports showing a help string next to the
59 value.
60
61 Arbitrary parameters can be passed when creating the object, and
62 accessed using ``item.attr``. If an attribute wasn't passed,
63 accessing it returns ``None``.
64
65 :param value: The completion suggestion.
66 :param type: Tells the shell script to provide special completion
67 support for the type. Click uses ``"dir"`` and ``"file"``.
68 :param help: String shown next to the value if supported.
69 :param kwargs: Arbitrary metadata. The built-in implementations
70 don't use this, but custom type completions paired with custom
71 shell support could use it.
72 """
73
74 __slots__ = ("value", "type", "help", "_info")
75
76 def __init__(
77 self,
78 value: t.Any,
79 type: str = "plain",
80 help: t.Optional[str] = None,
81 **kwargs: t.Any,
82 ) -> None:
83 self.value: t.Any = value
84 self.type: str = type
85 self.help: t.Optional[str] = help
86 self._info = kwargs
87
88 def __getattr__(self, name: str) -> t.Any:
89 return self._info.get(name)
90
91
92# Only Bash >= 4.4 has the nosort option.
93_SOURCE_BASH = """\
94%(complete_func)s() {
95 local IFS=$'\\n'
96 local response
97
98 response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \
99%(complete_var)s=bash_complete $1)
100
101 for completion in $response; do
102 IFS=',' read type value <<< "$completion"
103
104 if [[ $type == 'dir' ]]; then
105 COMPREPLY=()
106 compopt -o dirnames
107 elif [[ $type == 'file' ]]; then
108 COMPREPLY=()
109 compopt -o default
110 elif [[ $type == 'plain' ]]; then
111 COMPREPLY+=($value)
112 fi
113 done
114
115 return 0
116}
117
118%(complete_func)s_setup() {
119 complete -o nosort -F %(complete_func)s %(prog_name)s
120}
121
122%(complete_func)s_setup;
123"""
124
125_SOURCE_ZSH = """\
126#compdef %(prog_name)s
127
128%(complete_func)s() {
129 local -a completions
130 local -a completions_with_descriptions
131 local -a response
132 (( ! $+commands[%(prog_name)s] )) && return 1
133
134 response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \
135%(complete_var)s=zsh_complete %(prog_name)s)}")
136
137 for type key descr in ${response}; do
138 if [[ "$type" == "plain" ]]; then
139 if [[ "$descr" == "_" ]]; then
140 completions+=("$key")
141 else
142 completions_with_descriptions+=("$key":"$descr")
143 fi
144 elif [[ "$type" == "dir" ]]; then
145 _path_files -/
146 elif [[ "$type" == "file" ]]; then
147 _path_files -f
148 fi
149 done
150
151 if [ -n "$completions_with_descriptions" ]; then
152 _describe -V unsorted completions_with_descriptions -U
153 fi
154
155 if [ -n "$completions" ]; then
156 compadd -U -V unsorted -a completions
157 fi
158}
159
160if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
161 # autoload from fpath, call function directly
162 %(complete_func)s "$@"
163else
164 # eval/source/. command, register function for later
165 compdef %(complete_func)s %(prog_name)s
166fi
167"""
168
169_SOURCE_FISH = """\
170function %(complete_func)s;
171 set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \
172COMP_CWORD=(commandline -t) %(prog_name)s);
173
174 for completion in $response;
175 set -l metadata (string split "," $completion);
176
177 if test $metadata[1] = "dir";
178 __fish_complete_directories $metadata[2];
179 else if test $metadata[1] = "file";
180 __fish_complete_path $metadata[2];
181 else if test $metadata[1] = "plain";
182 echo $metadata[2];
183 end;
184 end;
185end;
186
187complete --no-files --command %(prog_name)s --arguments \
188"(%(complete_func)s)";
189"""
190
191
192class ShellComplete:
193 """Base class for providing shell completion support. A subclass for
194 a given shell will override attributes and methods to implement the
195 completion instructions (``source`` and ``complete``).
196
197 :param cli: Command being called.
198 :param prog_name: Name of the executable in the shell.
199 :param complete_var: Name of the environment variable that holds
200 the completion instruction.
201
202 .. versionadded:: 8.0
203 """
204
205 name: t.ClassVar[str]
206 """Name to register the shell as with :func:`add_completion_class`.
207 This is used in completion instructions (``{name}_source`` and
208 ``{name}_complete``).
209 """
210
211 source_template: t.ClassVar[str]
212 """Completion script template formatted by :meth:`source`. This must
213 be provided by subclasses.
214 """
215
216 def __init__(
217 self,
218 cli: BaseCommand,
219 ctx_args: t.MutableMapping[str, t.Any],
220 prog_name: str,
221 complete_var: str,
222 ) -> None:
223 self.cli = cli
224 self.ctx_args = ctx_args
225 self.prog_name = prog_name
226 self.complete_var = complete_var
227
228 @property
229 def func_name(self) -> str:
230 """The name of the shell function defined by the completion
231 script.
232 """
233 safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII)
234 return f"_{safe_name}_completion"
235
236 def source_vars(self) -> t.Dict[str, t.Any]:
237 """Vars for formatting :attr:`source_template`.
238
239 By default this provides ``complete_func``, ``complete_var``,
240 and ``prog_name``.
241 """
242 return {
243 "complete_func": self.func_name,
244 "complete_var": self.complete_var,
245 "prog_name": self.prog_name,
246 }
247
248 def source(self) -> str:
249 """Produce the shell script that defines the completion
250 function. By default this ``%``-style formats
251 :attr:`source_template` with the dict returned by
252 :meth:`source_vars`.
253 """
254 return self.source_template % self.source_vars()
255
256 def get_completion_args(self) -> t.Tuple[t.List[str], str]:
257 """Use the env vars defined by the shell script to return a
258 tuple of ``args, incomplete``. This must be implemented by
259 subclasses.
260 """
261 raise NotImplementedError
262
263 def get_completions(
264 self, args: t.List[str], incomplete: str
265 ) -> t.List[CompletionItem]:
266 """Determine the context and last complete command or parameter
267 from the complete args. Call that object's ``shell_complete``
268 method to get the completions for the incomplete value.
269
270 :param args: List of complete args before the incomplete value.
271 :param incomplete: Value being completed. May be empty.
272 """
273 ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args)
274 obj, incomplete = _resolve_incomplete(ctx, args, incomplete)
275 return obj.shell_complete(ctx, incomplete)
276
277 def format_completion(self, item: CompletionItem) -> str:
278 """Format a completion item into the form recognized by the
279 shell script. This must be implemented by subclasses.
280
281 :param item: Completion item to format.
282 """
283 raise NotImplementedError
284
285 def complete(self) -> str:
286 """Produce the completion data to send back to the shell.
287
288 By default this calls :meth:`get_completion_args`, gets the
289 completions, then calls :meth:`format_completion` for each
290 completion.
291 """
292 args, incomplete = self.get_completion_args()
293 completions = self.get_completions(args, incomplete)
294 out = [self.format_completion(item) for item in completions]
295 return "\n".join(out)
296
297
298class BashComplete(ShellComplete):
299 """Shell completion for Bash."""
300
301 name = "bash"
302 source_template = _SOURCE_BASH
303
304 @staticmethod
305 def _check_version() -> None:
306 import subprocess
307
308 output = subprocess.run(
309 ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE
310 )
311 match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode())
312
313 if match is not None:
314 major, minor = match.groups()
315
316 if major < "4" or major == "4" and minor < "4":
317 echo(
318 _(
319 "Shell completion is not supported for Bash"
320 " versions older than 4.4."
321 ),
322 err=True,
323 )
324 else:
325 echo(
326 _("Couldn't detect Bash version, shell completion is not supported."),
327 err=True,
328 )
329
330 def source(self) -> str:
331 self._check_version()
332 return super().source()
333
334 def get_completion_args(self) -> t.Tuple[t.List[str], str]:
335 cwords = split_arg_string(os.environ["COMP_WORDS"])
336 cword = int(os.environ["COMP_CWORD"])
337 args = cwords[1:cword]
338
339 try:
340 incomplete = cwords[cword]
341 except IndexError:
342 incomplete = ""
343
344 return args, incomplete
345
346 def format_completion(self, item: CompletionItem) -> str:
347 return f"{item.type},{item.value}"
348
349
350class ZshComplete(ShellComplete):
351 """Shell completion for Zsh."""
352
353 name = "zsh"
354 source_template = _SOURCE_ZSH
355
356 def get_completion_args(self) -> t.Tuple[t.List[str], str]:
357 cwords = split_arg_string(os.environ["COMP_WORDS"])
358 cword = int(os.environ["COMP_CWORD"])
359 args = cwords[1:cword]
360
361 try:
362 incomplete = cwords[cword]
363 except IndexError:
364 incomplete = ""
365
366 return args, incomplete
367
368 def format_completion(self, item: CompletionItem) -> str:
369 return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}"
370
371
372class FishComplete(ShellComplete):
373 """Shell completion for Fish."""
374
375 name = "fish"
376 source_template = _SOURCE_FISH
377
378 def get_completion_args(self) -> t.Tuple[t.List[str], str]:
379 cwords = split_arg_string(os.environ["COMP_WORDS"])
380 incomplete = os.environ["COMP_CWORD"]
381 args = cwords[1:]
382
383 # Fish stores the partial word in both COMP_WORDS and
384 # COMP_CWORD, remove it from complete args.
385 if incomplete and args and args[-1] == incomplete:
386 args.pop()
387
388 return args, incomplete
389
390 def format_completion(self, item: CompletionItem) -> str:
391 if item.help:
392 return f"{item.type},{item.value}\t{item.help}"
393
394 return f"{item.type},{item.value}"
395
396
397ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete])
398
399
400_available_shells: t.Dict[str, t.Type[ShellComplete]] = {
401 "bash": BashComplete,
402 "fish": FishComplete,
403 "zsh": ZshComplete,
404}
405
406
407def add_completion_class(
408 cls: ShellCompleteType, name: t.Optional[str] = None
409) -> ShellCompleteType:
410 """Register a :class:`ShellComplete` subclass under the given name.
411 The name will be provided by the completion instruction environment
412 variable during completion.
413
414 :param cls: The completion class that will handle completion for the
415 shell.
416 :param name: Name to register the class under. Defaults to the
417 class's ``name`` attribute.
418 """
419 if name is None:
420 name = cls.name
421
422 _available_shells[name] = cls
423
424 return cls
425
426
427def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]:
428 """Look up a registered :class:`ShellComplete` subclass by the name
429 provided by the completion instruction environment variable. If the
430 name isn't registered, returns ``None``.
431
432 :param shell: Name the class is registered under.
433 """
434 return _available_shells.get(shell)
435
436
437def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
438 """Determine if the given parameter is an argument that can still
439 accept values.
440
441 :param ctx: Invocation context for the command represented by the
442 parsed complete args.
443 :param param: Argument object being checked.
444 """
445 if not isinstance(param, Argument):
446 return False
447
448 assert param.name is not None
449 # Will be None if expose_value is False.
450 value = ctx.params.get(param.name)
451 return (
452 param.nargs == -1
453 or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE
454 or (
455 param.nargs > 1
456 and isinstance(value, (tuple, list))
457 and len(value) < param.nargs
458 )
459 )
460
461
462def _start_of_option(ctx: Context, value: str) -> bool:
463 """Check if the value looks like the start of an option."""
464 if not value:
465 return False
466
467 c = value[0]
468 return c in ctx._opt_prefixes
469
470
471def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool:
472 """Determine if the given parameter is an option that needs a value.
473
474 :param args: List of complete args before the incomplete value.
475 :param param: Option object being checked.
476 """
477 if not isinstance(param, Option):
478 return False
479
480 if param.is_flag or param.count:
481 return False
482
483 last_option = None
484
485 for index, arg in enumerate(reversed(args)):
486 if index + 1 > param.nargs:
487 break
488
489 if _start_of_option(ctx, arg):
490 last_option = arg
491
492 return last_option is not None and last_option in param.opts
493
494
495def _resolve_context(
496 cli: BaseCommand,
497 ctx_args: t.MutableMapping[str, t.Any],
498 prog_name: str,
499 args: t.List[str],
500) -> Context:
501 """Produce the context hierarchy starting with the command and
502 traversing the complete arguments. This only follows the commands,
503 it doesn't trigger input prompts or callbacks.
504
505 :param cli: Command being called.
506 :param prog_name: Name of the executable in the shell.
507 :param args: List of complete args before the incomplete value.
508 """
509 ctx_args["resilient_parsing"] = True
510 ctx = cli.make_context(prog_name, args.copy(), **ctx_args)
511 args = ctx.protected_args + ctx.args
512
513 while args:
514 command = ctx.command
515
516 if isinstance(command, MultiCommand):
517 if not command.chain:
518 name, cmd, args = command.resolve_command(ctx, args)
519
520 if cmd is None:
521 return ctx
522
523 ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True)
524 args = ctx.protected_args + ctx.args
525 else:
526 sub_ctx = ctx
527
528 while args:
529 name, cmd, args = command.resolve_command(ctx, args)
530
531 if cmd is None:
532 return ctx
533
534 sub_ctx = cmd.make_context(
535 name,
536 args,
537 parent=ctx,
538 allow_extra_args=True,
539 allow_interspersed_args=False,
540 resilient_parsing=True,
541 )
542 args = sub_ctx.args
543
544 ctx = sub_ctx
545 args = [*sub_ctx.protected_args, *sub_ctx.args]
546 else:
547 break
548
549 return ctx
550
551
552def _resolve_incomplete(
553 ctx: Context, args: t.List[str], incomplete: str
554) -> t.Tuple[t.Union[BaseCommand, Parameter], str]:
555 """Find the Click object that will handle the completion of the
556 incomplete value. Return the object and the incomplete value.
557
558 :param ctx: Invocation context for the command represented by
559 the parsed complete args.
560 :param args: List of complete args before the incomplete value.
561 :param incomplete: Value being completed. May be empty.
562 """
563 # Different shells treat an "=" between a long option name and
564 # value differently. Might keep the value joined, return the "="
565 # as a separate item, or return the split name and value. Always
566 # split and discard the "=" to make completion easier.
567 if incomplete == "=":
568 incomplete = ""
569 elif "=" in incomplete and _start_of_option(ctx, incomplete):
570 name, _, incomplete = incomplete.partition("=")
571 args.append(name)
572
573 # The "--" marker tells Click to stop treating values as options
574 # even if they start with the option character. If it hasn't been
575 # given and the incomplete arg looks like an option, the current
576 # command will provide option name completions.
577 if "--" not in args and _start_of_option(ctx, incomplete):
578 return ctx.command, incomplete
579
580 params = ctx.command.get_params(ctx)
581
582 # If the last complete arg is an option name with an incomplete
583 # value, the option will provide value completions.
584 for param in params:
585 if _is_incomplete_option(ctx, args, param):
586 return param, incomplete
587
588 # It's not an option name or value. The first argument without a
589 # parsed value will provide value completions.
590 for param in params:
591 if _is_incomplete_argument(ctx, param):
592 return param, incomplete
593
594 # There were no unparsed arguments, the command may be a group that
595 # will provide command name completions.
596 return ctx.command, incomplete