-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
662 additions
and
592 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
""" | ||
Typer_ and click_ provide tab-completion hooks for individual parameters. As with | ||
:mod:`~django_typer.parsers` custom completion logic can be implemented for custom | ||
parameter types and added to the annotation of the parameter. Previous versions of | ||
Typer_ supporting click_ 7 used the autocompletion argument to provide completion | ||
logic, Typer_ still supports this, but passing ``shell_complete`` to the annotation is | ||
the preferred way to do this. | ||
This package provides some completer functions and classes that work with common Django_ | ||
types: | ||
- **models**: Complete model object field strings using :class:`~django_typer.completers.model.ModelObjectCompleter`. | ||
- **django apps**: Complete app labels or names using :func:`~django_typer.completers.apps.app_labels`. | ||
- **commands**: Complete Django command names using :func:`~django_typer.completers.cmd.commands`. | ||
- **databases**: Complete Django database names using :func:`~django_typer.completers.db.databases`. | ||
- **import paths**: Complete Django database names using :func:`~django_typer.completers.path.import_paths`. | ||
- **paths**: Complete Django database names using :func:`~django_typer.completers.path.paths`. | ||
- **directories**: Complete Django database names using :func:`~django_typer.completers.path.directories`. | ||
""" | ||
|
||
import typing as t | ||
|
||
from click import Context, Parameter | ||
from click.core import ParameterSource | ||
from click.shell_completion import CompletionItem | ||
|
||
Completer = t.Callable[[Context, Parameter, str], t.List[CompletionItem]] | ||
Strings = t.Union[t.Sequence[str], t.KeysView[str], t.Generator[str, None, None]] | ||
|
||
|
||
def these_strings( | ||
strings: t.Union[t.Callable[[], Strings], Strings], | ||
allow_duplicates: bool = False, | ||
): | ||
""" | ||
Get a completer that provides completion logic that matches the allowed strings. | ||
:param strings: A sequence of allowed strings or a callable that generates a sequence of | ||
allowed strings. | ||
:param allow_duplicates: Whether or not to allow duplicate values. Defaults to False. | ||
:return: A completer function. | ||
""" | ||
|
||
def complete(ctx: Context, param: Parameter, incomplete: str): | ||
present = [] | ||
if ( | ||
not allow_duplicates | ||
and param.name | ||
and ctx.get_parameter_source(param.name) is not ParameterSource.DEFAULT | ||
): | ||
present = [value for value in (ctx.params.get(param.name) or [])] | ||
return [ | ||
CompletionItem(item) | ||
for item in (strings() if callable(strings) else strings) | ||
if item.startswith(incomplete) and item not in present | ||
] | ||
|
||
return complete | ||
|
||
|
||
def chain( | ||
completer: Completer, | ||
*completers: Completer, | ||
first_match: bool = False, | ||
allow_duplicates: bool = False, | ||
): | ||
""" | ||
Run through the given completers and return the items from the first one, or all | ||
of them if first_match is False. | ||
.. note:: | ||
This function is also useful for filtering out previously supplied duplicate | ||
values for completers that do not natively support that: | ||
.. code-block:: python | ||
shell_complete=chain( | ||
complete_import_path, | ||
allow_duplicates=False | ||
) | ||
:param completer: The first completer to use (must be at least one!) | ||
:param completers: The completers to use | ||
:param first_match: If true, return only the matches from the first completer that | ||
finds completions. Default: False | ||
:param allow_duplicates: If False (default) remove completions from previously provided | ||
values. | ||
""" | ||
|
||
def complete(ctx: Context, param: Parameter, incomplete: str): | ||
completions = [] | ||
present = [] | ||
if ( | ||
not allow_duplicates | ||
and param.name | ||
and ctx.get_parameter_source(param.name) is not ParameterSource.DEFAULT | ||
): | ||
present = [value for value in (ctx.params.get(param.name) or [])] | ||
for cmpltr in [completer, *completers]: | ||
completions.extend(cmpltr(ctx, param, incomplete)) | ||
if first_match and completions: | ||
break | ||
|
||
# eliminate duplicates | ||
return list( | ||
{ | ||
ci.value: ci | ||
for ci in completions | ||
if ci.value | ||
if ci.value not in present | ||
}.values() | ||
) | ||
|
||
return complete |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import typing as t | ||
|
||
from click import Context, Parameter | ||
from click.core import ParameterSource | ||
from click.shell_completion import CompletionItem | ||
from django.apps import apps | ||
|
||
|
||
def app_labels( | ||
ctx: Context, param: Parameter, incomplete: str | ||
) -> t.List[CompletionItem]: | ||
""" | ||
A case-sensitive completer for Django app labels or names. The completer | ||
prefers labels but names will also work. | ||
.. code-block:: python | ||
import typing as t | ||
import typer | ||
from django_typer.management import TyperCommand | ||
from django_typer.parsers import parse_app_label | ||
from django_typer.completers import complete_app_label | ||
class Command(TyperCommand): | ||
def handle( | ||
self, | ||
django_apps: t.Annotated[ | ||
t.List[AppConfig], | ||
typer.Argument( | ||
parser=parse_app_label, | ||
shell_complete=complete_app_label, | ||
help=_("One or more application labels.") | ||
) | ||
] | ||
): | ||
... | ||
:param ctx: The click context. | ||
:param param: The click parameter. | ||
:param incomplete: The incomplete string. | ||
:return: A list of matching app labels or names. Labels already present for the | ||
parameter on the command line will be filtered out. | ||
""" | ||
present = [] | ||
if ( | ||
param.name | ||
and ctx.get_parameter_source(param.name) is not ParameterSource.DEFAULT | ||
): | ||
present = [app.label for app in (ctx.params.get(param.name) or [])] | ||
ret = [ | ||
CompletionItem(app.label) | ||
for app in apps.get_app_configs() | ||
if app.label.startswith(incomplete) and app.label not in present | ||
] | ||
if not ret and incomplete: | ||
ret = [ | ||
CompletionItem(app.name) | ||
for app in apps.get_app_configs() | ||
if app.name.startswith(incomplete) | ||
and app.name not in present | ||
and app.label not in present | ||
] | ||
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from functools import partial | ||
|
||
from django.core.management import get_commands | ||
|
||
from . import these_strings | ||
|
||
commands = partial(these_strings, lambda: get_commands().keys()) | ||
""" | ||
A completer that completes management command names. | ||
:param allow_duplicates: Whether or not to allow duplicate values. Defaults to False. | ||
:return: A completer function. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from functools import partial | ||
|
||
from django.conf import settings | ||
|
||
from . import these_strings | ||
|
||
# use a function that returns a generator because we should not access settings on import | ||
databases = partial(these_strings, lambda: settings.DATABASES.keys()) | ||
""" | ||
A completer that completes Django database aliases configured in settings.DATABASES. | ||
:param allow_duplicates: Whether or not to allow duplicate values. Defaults to False. | ||
:return: A completer function. | ||
""" |
Oops, something went wrong.