diff options
author | David Lord <davidism@gmail.com> | 2021-04-23 08:38:47 -0700 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2021-04-23 08:38:47 -0700 |
commit | 0103c9570650daa59560baf42ad0a27e57b3157f (patch) | |
tree | 20c3c4ea3d69c24b91401881a903a4d822448dce /src/click/shell_completion.py | |
parent | 77ce48f8d7d3b64a09741cf53dd2995d668317cf (diff) | |
download | click-0103c9570650daa59560baf42ad0a27e57b3157f.tar.gz |
add typing annotations
Diffstat (limited to 'src/click/shell_completion.py')
-rw-r--r-- | src/click/shell_completion.py | 101 |
1 files changed, 65 insertions, 36 deletions
diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py index ae498ac..706fb69 100644 --- a/src/click/shell_completion.py +++ b/src/click/shell_completion.py @@ -4,14 +4,23 @@ import typing as t from gettext import gettext as _ from .core import Argument +from .core import BaseCommand +from .core import Context from .core import MultiCommand from .core import Option +from .core import Parameter from .core import ParameterSource from .parser import split_arg_string from .utils import echo -def shell_complete(cli, ctx_args, prog_name, complete_var, instruction): +def shell_complete( + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: """Perform shell completion for the given CLI program. :param cli: Command being called. @@ -64,13 +73,19 @@ class CompletionItem: __slots__ = ("value", "type", "help", "_info") - def __init__(self, value, type="plain", help=None, **kwargs): + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: self.value = value self.type = type self.help = help self._info = kwargs - def __getattr__(self, name): + def __getattr__(self, name: str) -> t.Any: return self._info.get(name) @@ -185,31 +200,38 @@ class ShellComplete: .. versionadded:: 8.0 """ - name: t.ClassVar[t.Optional[str]] = None + name: t.ClassVar[str] """Name to register the shell as with :func:`add_completion_class`. This is used in completion instructions (``{name}_source`` and ``{name}_complete``). """ - source_template: t.ClassVar[t.Optional[str]] = None + + source_template: t.ClassVar[str] """Completion script template formatted by :meth:`source`. This must be provided by subclasses. """ - def __init__(self, cli, ctx_args, prog_name, complete_var): + def __init__( + self, + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: self.cli = cli self.ctx_args = ctx_args self.prog_name = prog_name self.complete_var = complete_var @property - def func_name(self): + def func_name(self) -> str: """The name of the shell function defined by the completion script. """ safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), re.ASCII) return f"_{safe_name}_completion" - def source_vars(self): + def source_vars(self) -> t.Dict[str, t.Any]: """Vars for formatting :attr:`source_template`. By default this provides ``complete_func``, ``complete_var``, @@ -221,7 +243,7 @@ class ShellComplete: "prog_name": self.prog_name, } - def source(self): + def source(self) -> str: """Produce the shell script that defines the completion function. By default this ``%``-style formats :attr:`source_template` with the dict returned by @@ -229,14 +251,16 @@ class ShellComplete: """ return self.source_template % self.source_vars() - def get_completion_args(self): + def get_completion_args(self) -> t.Tuple[t.List[str], str]: """Use the env vars defined by the shell script to return a tuple of ``args, incomplete``. This must be implemented by subclasses. """ raise NotImplementedError - def get_completions(self, args, incomplete): + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: """Determine the context and last complete command or parameter from the complete args. Call that object's ``shell_complete`` method to get the completions for the incomplete value. @@ -245,14 +269,10 @@ class ShellComplete: :param incomplete: Value being completed. May be empty. """ ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) - - if ctx is None: - return [] - obj, incomplete = _resolve_incomplete(ctx, args, incomplete) return obj.shell_complete(ctx, incomplete) - def format_completion(self, item): + def format_completion(self, item: CompletionItem) -> str: """Format a completion item into the form recognized by the shell script. This must be implemented by subclasses. @@ -260,7 +280,7 @@ class ShellComplete: """ raise NotImplementedError - def complete(self): + def complete(self) -> str: """Produce the completion data to send back to the shell. By default this calls :meth:`get_completion_args`, gets the @@ -279,7 +299,7 @@ class BashComplete(ShellComplete): name = "bash" source_template = _SOURCE_BASH - def _check_version(self): + def _check_version(self) -> None: import subprocess output = subprocess.run(["bash", "--version"], stdout=subprocess.PIPE) @@ -300,11 +320,11 @@ class BashComplete(ShellComplete): _("Couldn't detect Bash version, shell completion is not supported.") ) - def source(self): + def source(self) -> str: self._check_version() return super().source() - def get_completion_args(self): + def get_completion_args(self) -> t.Tuple[t.List[str], str]: cwords = split_arg_string(os.environ["COMP_WORDS"]) cword = int(os.environ["COMP_CWORD"]) args = cwords[1:cword] @@ -326,7 +346,7 @@ class ZshComplete(ShellComplete): name = "zsh" source_template = _SOURCE_ZSH - def get_completion_args(self): + def get_completion_args(self) -> t.Tuple[t.List[str], str]: cwords = split_arg_string(os.environ["COMP_WORDS"]) cword = int(os.environ["COMP_CWORD"]) args = cwords[1:cword] @@ -348,7 +368,7 @@ class FishComplete(ShellComplete): name = "fish" source_template = _SOURCE_FISH - def get_completion_args(self): + def get_completion_args(self) -> t.Tuple[t.List[str], str]: cwords = split_arg_string(os.environ["COMP_WORDS"]) incomplete = os.environ["COMP_CWORD"] args = cwords[1:] @@ -367,14 +387,16 @@ class FishComplete(ShellComplete): return f"{item.type},{item.value}" -_available_shells = { +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { "bash": BashComplete, "fish": FishComplete, "zsh": ZshComplete, } -def add_completion_class(cls, name=None): +def add_completion_class( + cls: t.Type[ShellComplete], name: t.Optional[str] = None +) -> None: """Register a :class:`ShellComplete` subclass under the given name. The name will be provided by the completion instruction environment variable during completion. @@ -390,7 +412,7 @@ def add_completion_class(cls, name=None): _available_shells[name] = cls -def get_completion_class(shell): +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: """Look up a registered :class:`ShellComplete` subclass by the name provided by the completion instruction environment variable. If the name isn't registered, returns ``None``. @@ -400,7 +422,7 @@ def get_completion_class(shell): return _available_shells.get(shell) -def _is_incomplete_argument(ctx, param): +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: """Determine if the given parameter is an argument that can still accept values. @@ -411,6 +433,7 @@ def _is_incomplete_argument(ctx, param): if not isinstance(param, Argument): return False + assert param.name is not None value = ctx.params[param.name] return ( param.nargs == -1 @@ -423,12 +446,12 @@ def _is_incomplete_argument(ctx, param): ) -def _start_of_option(value): +def _start_of_option(value: str) -> bool: """Check if the value looks like the start of an option.""" - return value and not value[0].isalnum() + return not value[0].isalnum() if value else False -def _is_incomplete_option(args, param): +def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: """Determine if the given parameter is an option that needs a value. :param args: List of complete args before the incomplete value. @@ -452,7 +475,9 @@ def _is_incomplete_option(args, param): return last_option is not None and last_option in param.opts -def _resolve_context(cli, ctx_args, prog_name, args): +def _resolve_context( + cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str] +) -> Context: """Produce the context hierarchy starting with the command and traversing the complete arguments. This only follows the commands, it doesn't trigger input prompts or callbacks. @@ -466,9 +491,11 @@ def _resolve_context(cli, ctx_args, prog_name, args): args = ctx.protected_args + ctx.args while args: - if isinstance(ctx.command, MultiCommand): - if not ctx.command.chain: - name, cmd, args = ctx.command.resolve_command(ctx, args) + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) if cmd is None: return ctx @@ -477,7 +504,7 @@ def _resolve_context(cli, ctx_args, prog_name, args): args = ctx.protected_args + ctx.args else: while args: - name, cmd, args = ctx.command.resolve_command(ctx, args) + name, cmd, args = command.resolve_command(ctx, args) if cmd is None: return ctx @@ -493,14 +520,16 @@ def _resolve_context(cli, ctx_args, prog_name, args): args = sub_ctx.args ctx = sub_ctx - args = sub_ctx.protected_args + sub_ctx.args + args = [*sub_ctx.protected_args, *sub_ctx.args] else: break return ctx -def _resolve_incomplete(ctx, args, incomplete): +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: """Find the Click object that will handle the completion of the incomplete value. Return the object and the incomplete value. |