Shell Tab-Completions

_images/closepoll_example.gif

Shell completions are helpful suggestions that are displayed when you press the <TAB> key while typing a command in a shell. They are especially useful when you are not sure about the exact name of a command, its options, its arguments or the potential values of either.

Django has some support for bash completions, but it is not enabled by default and left to the user to install.

django-typer augments the upstream functionality of Typer and Click to provide both an easy way to define shell completions for your custom CLI options and arguments as well as a way to install them in your shell.

Note

django-typer supports shell completion installation for bash, zsh, fish and powershell.

Installation

Each shell has its own mechanism for enabling completions and this is further complicated by how different shells are installed and configured on different platforms. All shells have the same basic process. Completion logic needs to be registered with the shell that will be invoked when tabs are pressed for a specific command or script. To install tab completions for django commands we need to register our completion logic for Django manage script with the shell. This process has two phases:

  1. Ensure that your shell is configured to support completions.

  2. Use the shellcompletion command to install the completion hook for your Django manage script. This usually entails adding a specifically named script to a certain directory or adding lines to an existing script. The shellcompletion command will handle this for you.

The goal of this guide is not to be an exhaustive list of how to enable completions for each supported shell on all possible platforms, but rather to provide general guidance on how to enable completions for the most common platforms and environments. If you encounter issues or have solutions, please report them on our issues page

Windows

powershell is now bundled with most modern versions of Windows. There should be no additional installation steps necessary, but please refer to the Windows documentation if powershell is not present on your system.

Linux

bash is the default shell on most Linux distributions. Completions should be enabled by default.

OSX

zsh is currently the default shell on OSX. Unfortunately completions are not supported out of the box. We recommend using homebrew to install the zsh-completions package.

After installing the package you will need to add some configuration to your .zshrc file. We have had luck with the following:

zstyle ':completion:*' menu select

if type brew &>/dev/null; then
    FPATH=~/.zfunc:$(brew --prefix)/share/zsh-completions:$FPATH

    autoload -Uz compinit
    compinit
fi

fpath+=~/.zfunc

Install the Completion Hook

django-typer comes with a management command called shellcompletion. To install completions for your Django project simply run the install command:

./manage.py shellcompletion install

Note

The manage script may be named differently in your project - this is fine. The only requirement is that you invoke the shellcompletion command in the same way you would invoke any commands you would like tab completions to work for.

The installation script should be able to automatically detect your shell and install the appropriate scripts. If it is unable to do so you may force it to install for a specific shell by passing the shell name as an argument. Refer to the shellcompletion for details.

After installation you will need to restart your shell or source the appropriate rc file.

Warning

In production environments it is recommended that your management script be installed as a command on your system path. This will produce the most reliable installation.

Enabling Completions in Development Environments

Most shells work best when the manage script is installed as an executable on the system path. This is not always the case, especially in development environments. In these scenarios completion installation should still work, but you may need to always invoke the script from the same path. Fish may not work at all in this mode.

Integrating with Other CLI Completion Libraries

When tab completion is requested for a command that is not a TyperCommand, django-typer will delegate that request to Django’s autocomplete function as a fallback. This means that using django-typer to install completion scripts will enable completions for Django BaseCommands in all supported shells.

However, if you are using a separate package to define custom tab completions for your commands you may use the –fallback parameter to supply a separate fallback hook that will invoke the appropriate completion function for your commands. If there are other popular completion libraries please consider letting us know or submitting a PR to support these libraries as a fallback out of the box.

The long-term solution here should be that Django itself manages completion installation and provides hooks for implementing libraries to provide completions for their own commands.

Defining Custom Completions

To define custom completion logic for your arguments and options pass the shell_completion parameter in your type hint annotations. django-typer comes with a few provided completers for common Django types. One of the provided completers completes Django app labels and names. We might build a similar completer that only works for Django app labels like this:

 1import typing as t
 2import typer
 3from click import Context, Parameter
 4from click.shell_completion import CompletionItem
 5from django.apps import apps
 6
 7from django_typer import TyperCommand
 8
 9
10# the completer function signature must match this exactly
11def complete_app_label(
12    ctx: Context,
13    param: Parameter,
14    incomplete: str
15) -> t.List[CompletionItem]:
16
17    # don't offer apps that are already present as completion suggestions
18    present = [app.label for app in (ctx.params.get(param.name or "") or [])]
19    return [
20        CompletionItem(app.label)
21        for app in apps.get_app_configs()
22        if app.label.startswith(incomplete) and app.label not in present
23    ]
24
25
26class MyCommand(TyperCommand):
27
28    @command()
29    def handle(
30        self,
31        apps: t.Annotated[
32            t.List[str],
33            typer.Argument(
34                help="The app label",
35                shell_complete=complete_app_label  # pass the completer function here
36            )
37        ]
38    ):
39        pass

Tip

See the ModelObjectCompleter for a completer that works for many Django model field types.

Debugging Tab Completers

Debugging tab completion code can be tricky because when invoked in situ in the shell the completer code is run as a subprocess and it’s output is captured by the shell. This means you can’t set a breakpoint and enter into the debugger easily.

To help with this django-typer provides a debug mode that will enter into the tab-completion logic flow. Use the shellcompletion complete() command, to pass the command line string that you would like to debug. For example:

./manage.py shellcompletion complete "mycommand --"