summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst6
-rw-r--r--docs/shell-completion.rst7
-rw-r--r--examples/bashcompletion/README12
-rw-r--r--examples/bashcompletion/bashcompletion.py45
-rw-r--r--examples/completion/README28
-rw-r--r--examples/completion/completion.py53
-rw-r--r--examples/completion/setup.py (renamed from examples/bashcompletion/setup.py)6
-rw-r--r--src/click/core.py65
-rw-r--r--src/click/types.py12
-rw-r--r--tests/test_shell_completion.py17
10 files changed, 166 insertions, 85 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 54bcefe..e6d5c23 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -98,6 +98,12 @@ Unreleased
completions suggestions.
- Groups complete the names commands were registered with, which
can differ from the name they were created with.
+ - The ``autocompletion`` parameter for options and arguments is
+ renamed to ``shell_complete``. The function must take four
+ parameters ``ctx, param, args, incomplete``, must do matching
+ rather than return all values, and must return a list of strings
+ or a list of ``ShellComplete``. The old name and behavior is
+ deprecated and will be removed in 8.1.
Version 7.1.2
diff --git a/docs/shell-completion.rst b/docs/shell-completion.rst
index 739a7c7..a02c3be 100644
--- a/docs/shell-completion.rst
+++ b/docs/shell-completion.rst
@@ -148,11 +148,12 @@ Overriding Value Completion
---------------------------
Value completions for a parameter can be customized without a custom
-type by providing an ``autocompletion`` function. The function is used
+type by providing a ``shell_complete`` function. The function is used
instead of any completion provided by the type. It is passed 3 keyword
arguments:
- ``ctx`` - The current command context.
+- ``param`` - The current parameter requesting completion.
- ``args`` - The list of complete args before the incomplete value.
- ``incomplete`` - The partial word that is being completed. May
be an empty string if no characters have been entered yet.
@@ -165,11 +166,11 @@ start with the incomplete value.
.. code-block:: python
- def complete_env_vars(ctx, args, incomplete):
+ def complete_env_vars(ctx, param, args, incomplete):
return [k for k in os.environ if k.startswith(incomplete)]
@click.command()
- @click.argument("name", autocompletion=complete_env_vars)
+ @click.argument("name", shell_complete=complete_env_vars)
def cli(name):
click.echo(f"Name: {name}")
click.echo(f"Value: {os.environ[name]}")
diff --git a/examples/bashcompletion/README b/examples/bashcompletion/README
deleted file mode 100644
index f8a0d51..0000000
--- a/examples/bashcompletion/README
+++ /dev/null
@@ -1,12 +0,0 @@
-$ bashcompletion
-
- bashcompletion is a simple example of an application that
- tries to autocomplete commands, arguments and options.
-
- This example requires Click 2.0 or higher.
-
-Usage:
-
- $ pip install --editable .
- $ eval "$(_BASHCOMPLETION_COMPLETE=source bashcompletion)"
- $ bashcompletion --help
diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py
deleted file mode 100644
index 3f8c9df..0000000
--- a/examples/bashcompletion/bashcompletion.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import os
-
-import click
-
-
-@click.group()
-def cli():
- pass
-
-
-def get_env_vars(ctx, args, incomplete):
- # Completions returned as strings do not have a description displayed.
- for key in os.environ.keys():
- if incomplete in key:
- yield key
-
-
-@cli.command(help="A command to print environment variables")
-@click.argument("envvar", type=click.STRING, autocompletion=get_env_vars)
-def cmd1(envvar):
- click.echo(f"Environment variable: {envvar}")
- click.echo(f"Value: {os.environ[envvar]}")
-
-
-@click.group(help="A group that holds a subcommand")
-def group():
- pass
-
-
-def list_users(ctx, args, incomplete):
- # You can generate completions with descriptions by returning
- # tuples in the form (completion, description).
- users = [("bob", "butcher"), ("alice", "baker"), ("jerry", "candlestick maker")]
- # Ths will allow completion matches based on matches within the
- # description string too!
- return [user for user in users if incomplete in user[0] or incomplete in user[1]]
-
-
-@group.command(help="Choose a user")
-@click.argument("user", type=click.STRING, autocompletion=list_users)
-def subcmd(user):
- click.echo(f"Chosen user is {user}")
-
-
-cli.add_command(group)
diff --git a/examples/completion/README b/examples/completion/README
new file mode 100644
index 0000000..f15654e
--- /dev/null
+++ b/examples/completion/README
@@ -0,0 +1,28 @@
+$ completion
+============
+
+Demonstrates Click's shell completion support.
+
+.. code-block:: bash
+
+ pip install --editable .
+
+For Bash:
+
+.. code-block:: bash
+
+ eval "$(_COMPLETION_COMPLETE=source_bash completion)"
+
+For Zsh:
+
+.. code-block:: zsh
+
+ eval "$(_COMPLETION_COMPLETE=source_zsh completion)"
+
+For Fish:
+
+.. code-block:: fish
+
+ eval (env _COMPLETION_COMPLETE=source_fish completion)
+
+Now press tab (maybe twice) after typing something to see completions.
diff --git a/examples/completion/completion.py b/examples/completion/completion.py
new file mode 100644
index 0000000..92dcc74
--- /dev/null
+++ b/examples/completion/completion.py
@@ -0,0 +1,53 @@
+import os
+
+import click
+from click.shell_completion import CompletionItem
+
+
+@click.group()
+def cli():
+ pass
+
+
+@cli.command()
+@click.option("--dir", type=click.Path(file_okay=False))
+def ls(dir):
+ click.echo("\n".join(os.listdir(dir)))
+
+
+def get_env_vars(ctx, param, args, incomplete):
+ # Returning a list of values is a shortcut to returning a list of
+ # CompletionItem(value).
+ return [k for k in os.environ if incomplete in k]
+
+
+@cli.command(help="A command to print environment variables")
+@click.argument("envvar", shell_complete=get_env_vars)
+def show_env(envvar):
+ click.echo(f"Environment variable: {envvar}")
+ click.echo(f"Value: {os.environ[envvar]}")
+
+
+@cli.group(help="A group that holds a subcommand")
+def group():
+ pass
+
+
+def list_users(ctx, args, incomplete):
+ # You can generate completions with help strings by returning a list
+ # of CompletionItem. You can match on whatever you want, including
+ # the help.
+ items = [("bob", "butcher"), ("alice", "baker"), ("jerry", "candlestick maker")]
+
+ for value, help in items:
+ if incomplete in value or incomplete in help:
+ yield CompletionItem(value, help=help)
+
+
+@group.command(help="Choose a user")
+@click.argument("user", type=click.STRING, autocompletion=list_users)
+def select_user(user):
+ click.echo(f"Chosen user is {user}")
+
+
+cli.add_command(group)
diff --git a/examples/bashcompletion/setup.py b/examples/completion/setup.py
index f9a2c29..a78d140 100644
--- a/examples/bashcompletion/setup.py
+++ b/examples/completion/setup.py
@@ -1,13 +1,13 @@
from setuptools import setup
setup(
- name="click-example-bashcompletion",
+ name="click-example-completion",
version="1.0",
- py_modules=["bashcompletion"],
+ py_modules=["completion"],
include_package_data=True,
install_requires=["click"],
entry_points="""
[console_scripts]
- bashcompletion=bashcompletion:cli
+ completion=completion:cli
""",
)
diff --git a/src/click/core.py b/src/click/core.py
index 6e45beb..cccf939 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -1781,9 +1781,17 @@ class Parameter:
order of processing.
:param envvar: a string or list of strings that are environment variables
that should be checked.
- :param autocompletion: A function that returns custom shell
+ :param shell_complete: A function that returns custom shell
completions. Used instead of the param's type completion if
- given.
+ given. Takes ``ctx, param, args, incomplete`` and returns a list
+ of :class:`~click.shell_completion.CompletionItem` or a list of
+ strings.
+
+ .. versionchanged:: 8.0
+ ``autocompletion`` is renamed to ``shell_complete`` and has new
+ semantics described in the docs above. The old name is
+ deprecated and will be removed in 8.1, until then it will be
+ wrapped to match the new requirements.
.. versionchanged:: 7.1
Empty environment variables are ignored rather than taking the
@@ -1795,6 +1803,7 @@ class Parameter:
parameter. The old callback format will still work, but it will
raise a warning to give you a chance to migrate the code easier.
"""
+
param_type_name = "parameter"
def __init__(
@@ -1809,6 +1818,7 @@ class Parameter:
expose_value=True,
is_eager=False,
envvar=None,
+ shell_complete=None,
autocompletion=None,
):
self.name, self.opts, self.secondary_opts = self._parse_decls(
@@ -1834,7 +1844,35 @@ class Parameter:
self.is_eager = is_eager
self.metavar = metavar
self.envvar = envvar
- self.autocompletion = autocompletion
+
+ if autocompletion is not None:
+ import warnings
+
+ warnings.warn(
+ "'autocompletion' is renamed to 'shell_complete'. The old name is"
+ " deprecated and will be removed in Click 8.1. See the docs about"
+ " 'Parameter' for information about new behavior.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ def shell_complete(ctx, param, args, incomplete):
+ from click.shell_completion import CompletionItem
+
+ out = []
+
+ for c in autocompletion(ctx, args, incomplete):
+ if isinstance(c, tuple):
+ c = CompletionItem(c[0], help=c[1])
+ elif isinstance(c, str):
+ c = CompletionItem(c)
+
+ if c.value.startswith(incomplete):
+ out.append(c)
+
+ return out
+
+ self._custom_shell_complete = shell_complete
def to_info_dict(self):
"""Gather information that could be useful for a tool generating
@@ -2025,8 +2063,8 @@ class Parameter:
return " / ".join(repr(x) for x in hint_list)
def shell_complete(self, ctx, args, incomplete):
- """Return a list of completions for the incomplete value. If an
- :attr:`autocompletion` function was given, it is used.
+ """Return a list of completions for the incomplete value. If a
+ ``shell_complete`` function was given during init, it is used.
Otherwise, the :attr:`type`
:meth:`~click.types.ParamType.shell_complete` function is used.
@@ -2036,22 +2074,17 @@ class Parameter:
.. versionadded:: 8.0
"""
- from click.shell_completion import CompletionItem
+ if self._custom_shell_complete is not None:
+ results = self._custom_shell_complete(ctx, self, args, incomplete)
- if self.autocompletion is not None:
- results = []
+ if results and isinstance(results[0], str):
+ from click.shell_completion import CompletionItem
- for c in self.autocompletion(ctx=ctx, args=args, incomplete=incomplete):
- if isinstance(c, CompletionItem):
- results.append(c)
- elif isinstance(c, tuple):
- results.append(CompletionItem(c[0], help=c[1]))
- else:
- results.append(CompletionItem(c))
+ results = [CompletionItem(c) for c in results]
return results
- return self.type.shell_complete(ctx, args, incomplete)
+ return self.type.shell_complete(ctx, self, args, incomplete)
class Option(Parameter):
diff --git a/src/click/types.py b/src/click/types.py
index 4cedc27..f26b8b5 100644
--- a/src/click/types.py
+++ b/src/click/types.py
@@ -107,7 +107,7 @@ class ParamType:
"""Helper method to fail with an invalid value message."""
raise BadParameter(message, ctx=ctx, param=param)
- def shell_complete(self, ctx, args, incomplete):
+ def shell_complete(self, ctx, param, args, incomplete):
"""Return a list of
:class:`~click.shell_completion.CompletionItem` objects for the
incomplete value. Most types do not provide completions, but
@@ -115,6 +115,7 @@ class ParamType:
completions as well.
:param ctx: Invocation context for this command.
+ :param param: The parameter that is requesting completion.
:param args: List of complete args before the incomplete value.
:param incomplete: Value being completed. May be empty.
@@ -264,10 +265,11 @@ class Choice(ParamType):
def __repr__(self):
return f"Choice({list(self.choices)})"
- def shell_complete(self, ctx, args, incomplete):
+ def shell_complete(self, ctx, param, args, incomplete):
"""Complete choices that start with the incomplete value.
:param ctx: Invocation context for this command.
+ :param param: The parameter that is requesting completion.
:param args: List of complete args before the incomplete value.
:param incomplete: Value being completed. May be empty.
@@ -612,11 +614,12 @@ class File(ParamType):
ctx,
)
- def shell_complete(self, ctx, args, incomplete):
+ def shell_complete(self, ctx, param, args, incomplete):
"""Return a special completion marker that tells the completion
system to use the shell to provide file path completions.
:param ctx: Invocation context for this command.
+ :param param: The parameter that is requesting completion.
:param args: List of complete args before the incomplete value.
:param incomplete: Value being completed. May be empty.
@@ -757,12 +760,13 @@ class Path(ParamType):
return self.coerce_path_result(rv)
- def shell_complete(self, ctx, args, incomplete):
+ def shell_complete(self, ctx, param, args, incomplete):
"""Return a special completion marker that tells the completion
system to use the shell to provide path completions for only
directories or any paths.
:param ctx: Invocation context for this command.
+ :param param: The parameter that is requesting completion.
:param args: List of complete args before the incomplete value.
:param incomplete: Value being completed. May be empty.
diff --git a/tests/test_shell_completion.py b/tests/test_shell_completion.py
index e40d08a..e2d865c 100644
--- a/tests/test_shell_completion.py
+++ b/tests/test_shell_completion.py
@@ -114,7 +114,7 @@ def test_option_flag():
def test_option_custom():
- def custom(ctx, args, incomplete):
+ def custom(ctx, param, args, incomplete):
return [incomplete.upper()]
cli = Command(
@@ -122,13 +122,26 @@ def test_option_custom():
params=[
Argument(["x"]),
Argument(["y"]),
- Argument(["z"], autocompletion=custom),
+ Argument(["z"], shell_complete=custom),
],
)
assert _get_words(cli, ["a", "b"], "") == [""]
assert _get_words(cli, ["a", "b"], "c") == ["C"]
+def test_autocompletion_deprecated():
+ # old function takes three params, returns all values, can mix
+ # strings and tuples
+ def custom(ctx, args, incomplete):
+ return [("art", "x"), "bat", "cat"]
+
+ with pytest.deprecated_call():
+ cli = Command("cli", params=[Argument(["x"], autocompletion=custom)])
+
+ assert _get_words(cli, [], "") == ["art", "bat", "cat"]
+ assert _get_words(cli, [], "c") == ["cat"]
+
+
def test_option_multiple():
cli = Command(
"type",