Source code for django_typer.completers

"""
Typer_ and :doc:`click <click:index>` 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 :doc:`click <click:index>` 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 python import paths using
  :func:`~django_typer.completers.path.import_paths`.
- **paths**: Complete file and directory paths using
  :func:`~django_typer.completers.path.paths`.
- **directories**: Complete directories using
  :func:`~django_typer.completers.path.directories`.
- **media paths**: Complete Django media paths using
  :func:`~django_typer.completers.path.media_paths`.
- **static paths**: Complete Django static paths using
  :func:`~django_typer.completers.path.static_paths`.
- **languages**: Complete Django language names using
  :func:`~django_typer.completers.settings.languages`.
- **settings**: Complete Django settings variable names using
  :func:`~django_typer.completers.settings.setting`.
"""

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]]

ItemTuple = t.Union[t.Any, t.Tuple[t.Any, t.Any]]
Strings = t.Union[
    t.Sequence[ItemTuple],
    t.KeysView[t.Any],
    t.ValuesView[t.Any],
    t.ItemsView[t.Any, t.Any],
    t.Generator[ItemTuple, None, None],
]


[docs] 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. If the sequence contains 2-tuples the second element is used as the help text for the completion item. :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 = [] items = [] 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 item in strings() if callable(strings) else strings: help = None if isinstance(item, tuple): item, help = str(item[0]), str(item[1]) if item.startswith(incomplete) and item not in present: items.append(CompletionItem(item, help=help)) return items return complete
[docs] 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