Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pass extra context settings to completion #1679

Merged
merged 1 commit into from
Oct 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Unreleased
parameter. :issue:`1264`, :pr:`1329`
- Add an optional parameter to ``ProgressBar.update`` to set the
``current_item``. :issue:`1226`, :pr:`1332`
- Include ``--help`` option in completion. :pr:`1504`
- ``version_option`` uses ``importlib.metadata`` (or the
``importlib_metadata`` backport) instead of ``pkg_resources``.
:issue:`1582`
Expand Down Expand Up @@ -105,6 +104,10 @@ Unreleased
``ShellComplete``. The old name and behavior is deprecated and
will be removed in 8.1.

- Extra context settings (``obj=...``, etc.) are passed on to the
completion system. :issue:`942`
- Include ``--help`` option in completion. :pr:`1504`


Version 7.1.2
-------------
Expand Down
6 changes: 3 additions & 3 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ def main(
prog_name = _detect_program_name()

# Process shell completion requests and exit early.
self._main_shell_completion(prog_name, complete_var)
self._main_shell_completion(extra, prog_name, complete_var)

try:
try:
Expand Down Expand Up @@ -1000,7 +1000,7 @@ def main(
echo("Aborted!", file=sys.stderr)
sys.exit(1)

def _main_shell_completion(self, prog_name, complete_var=None):
def _main_shell_completion(self, ctx_args, prog_name, complete_var=None):
"""Check if the shell is asking for tab completion, process
that, then exit early. Called from :meth:`main` before the
program is invoked.
Expand All @@ -1020,7 +1020,7 @@ def _main_shell_completion(self, prog_name, complete_var=None):

from .shell_completion import shell_complete

rv = shell_complete(self, prog_name, complete_var, instruction)
rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction)
_fast_exit(rv)

def __call__(self, *args, **kwargs):
Expand Down
16 changes: 10 additions & 6 deletions src/click/shell_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
from .utils import echo


def shell_complete(cli, prog_name, complete_var, instruction):
def shell_complete(cli, ctx_args, prog_name, complete_var, instruction):
"""Perform shell completion for the given CLI program.

:param cli: Command being called.
:param ctx_args: Extra arguments to pass to
``cli.make_context``.
:param prog_name: Name of the executable in the shell.
:param complete_var: Name of the environment variable that holds
the completion instruction.
Expand All @@ -25,7 +27,7 @@ def shell_complete(cli, prog_name, complete_var, instruction):
if comp_cls is None:
return 1

comp = comp_cls(cli, prog_name, complete_var)
comp = comp_cls(cli, ctx_args, prog_name, complete_var)

if instruction == "source":
echo(comp.source())
Expand Down Expand Up @@ -190,8 +192,9 @@ class ShellComplete:
be provided by subclasses.
"""

def __init__(self, cli, prog_name, complete_var):
def __init__(self, cli, ctx_args, prog_name, complete_var):
self.cli = cli
self.ctx_args = ctx_args
self.prog_name = prog_name
self.complete_var = complete_var

Expand Down Expand Up @@ -238,7 +241,7 @@ def get_completions(self, args, incomplete):
:param args: List of complete args before the incomplete value.
:param incomplete: Value being completed. May be empty.
"""
ctx = _resolve_context(self.cli, self.prog_name, args)
ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args)

if ctx is None:
return []
Expand Down Expand Up @@ -446,7 +449,7 @@ def _is_incomplete_option(args, param):
return last_option is not None and last_option in param.opts


def _resolve_context(cli, prog_name, args):
def _resolve_context(cli, ctx_args, prog_name, args):
"""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.
Expand All @@ -455,7 +458,8 @@ def _resolve_context(cli, prog_name, args):
:param prog_name: Name of the executable in the shell.
:param args: List of complete args before the incomplete value.
"""
ctx = cli.make_context(prog_name, args.copy(), resilient_parsing=True)
ctx_args["resilient_parsing"] = True
ctx = cli.make_context(prog_name, args.copy(), **ctx_args)
args = ctx.protected_args + ctx.args

while args:
Expand Down
16 changes: 15 additions & 1 deletion tests/test_shell_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


def _get_completions(cli, args, incomplete):
comp = ShellComplete(cli, cli.name, "_CLICK_COMPLETE")
comp = ShellComplete(cli, {}, cli.name, "_CLICK_COMPLETE")
return comp.get_completions(args, incomplete)


Expand Down Expand Up @@ -275,3 +275,17 @@ def test_full_complete(runner, shell, env, expect):
env["_CLI_COMPLETE"] = f"complete_{shell}"
result = runner.invoke(cli, env=env)
assert result.output == expect


@pytest.mark.usefixtures("_patch_for_completion")
def test_context_settings(runner):
def complete(ctx, param, incomplete):
return ctx.obj["choices"]

cli = Command("cli", params=[Argument("x", shell_complete=complete)])
result = runner.invoke(
cli,
obj={"choices": ["a", "b"]},
env={"COMP_WORDS": "", "COMP_CWORD": "0", "_CLI_COMPLETE": "complete_bash"},
)
assert result.output == "plain,a\nplain,b\n"