typer-static-completion
Generate static shell completions for typer applications. Requires Python 3.11 or newer.
Status: initial Bash, Fish, and Zsh implementation. Typer introspection and generation work
for nested commands, flags, choices, tuple options, scalar/tuple/variadic arguments, and paths.
The CLI provides generate; the Python API provides generate() and write().
PowerShell and dynamic delegation remain unimplemented. See
TODO.md for the remaining work.
from typer_static_completion import generate
from myapp.cli import app
script = generate(app, "myapp", "bash")
Pass "fish" or "zsh" to target those shells. Write the returned script to a
file and source it in the corresponding shell (after compinit for Zsh). Completion stays
static: dynamic callback values are omitted by default. Explicit file fallback
is supported; hybrid delegation currently raises an error. The supported
Typer 0.26 parser accepts chain=True but does not execute chained commands.
Generation rejects this setting explicitly, including callback and add_typer
settings, before Typer discards it during command conversion. Chain completion
remains deferred until the supported parser can execute chains.
Tuple options such as pair: tuple[Color, Path] complete each value using its
own type. All three shells support --pair blue path, --pair=blue path,
attached short values, and repeated occurrences. Param.values holds the
per-position ValueSpec metadata for callers constructing command trees by hand.
Tuple positional arguments also complete each position using its own type;
options may appear between values, and subsequent scalar or variadic arguments
receive their own completions.
Groups can take scalar, tuple, or variadic arguments. Completion consumes their
values before offering subcommands, then switches to the child's scope. Like
Typer's default parser, group options must precede the first argument; a child
starts its own option parsing. Optional arguments still consume available words,
and variadic group arguments consume the remainder, including command names.
Custom groups with allow_interspersed_args=True are diagnosed as unsupported.
Choices configured with case_sensitive=False accept differently cased prefixes
and insert the declared spelling, including in tuple parameters. Case-sensitive
choices retain exact prefix matching. Matching uses lowercase prefixes, as in
Typer's completion, with non-ASCII casing governed by the shell locale.
The public Shell enum lists the three implemented shells. Generator subclasses
implement render() and quote(); custom generators can be registered under
additional string names. verify.check_syntax() checks scripts with a locally
installed shell's parser.
Command models are frozen, and each Command copies its subcommand mapping into
a read-only view. Changing the original dictionary does not change the tree.
Declaration order is preserved. Trees remain unhashable; use dataclasses.replace()
to construct modified versions. Subcommand views are not mutable dictionaries.
Build-time generation and example
Run the example from this checkout:
pixi run example deploy --environment staging
pixi run example-completions
The second task calls write() from
examples/completions.py, producing
build/completions/bash/shipyard, build/completions/zsh/_shipyard, and
build/completions/fish/shipyard.fish. The example is exercised by the CI test
suite. In your project, call the same API during your build or release process:
from typer_static_completion import GenerationOptions, write
from myapp.cli import app
outputs = write(
app,
"myapp",
output_dir="build/completions",
options=GenerationOptions(regenerate_command="pixi run completions"),
)
Use shells=["fish"] to select shells, dry_run=True to preview the returned
path-to-content mapping without writes, or
layout={"bash": "share/bash-completion/completions/{prog}"} to override a
shell's destination beneath the output directory. Unchanged files keep their
modification times. Generation and destination checks finish before writing;
changed files are replaced individually, so an I/O failure can leave a partially
updated set. Custom paths cannot escape the output directory or collide.
To try completion in an interactive shell, run these commands from the checkout
in the corresponding shell. The shipyard function supplies the example command;
a packaged application would supply its own console entrypoint.
Bash:
shipyard() { pixi run example "$@"; }
source build/completions/bash/shipyard
Zsh:
shipyard() { pixi run example "$@"; }
autoload -Uz compinit
compinit
source build/completions/zsh/_shipyard
Fish:
function shipyard
pixi run example $argv
end
source build/completions/fish/shipyard.fish
Try typing shipyard deploy --environment st followed by TAB. Completion itself
runs entirely in the shell. For persistent installation, copy the Bash file to
~/.local/share/bash-completion/completions/shipyard when using bash-completion,
or source it from your Bash startup file. Copy the Fish file to
~/.config/fish/completions/shipyard.fish (or your $XDG_CONFIG_HOME equivalent).
For Zsh, copy _shipyard to a directory on fpath before calling compinit.
These installation steps are manual; generation does not edit shell profiles.
After changing the CLI, rerun pixi run example-completions and source or install
the updated files. For your own application, replace that task with your build's
generation command. The banner records the regeneration command for reference.
Command line interface
Generate one shell's script from an importable Typer app:
pixi run typer-static-completion generate myapp.cli:app --prog-name myapp --shell fish -o myapp.fish
generate requires --shell and --prog-name. Omit -o (or use -o -) to emit
the script on stdout. Otherwise, it writes to exactly the specified path, creating
parent directories as needed. Relative paths are relative to the current working
directory. Import output goes to stderr so it cannot corrupt the generated script.
Targets must point to Typer instances, such as myapp.cli:app; wrapper functions
and factories are never called to discover an app. Targets must already be
importable in the current environment. For factory-backed applications, construct
the app explicitly and use the Python API.
Exit codes are 0 for success and 2 for usage or operation errors. There is no project discovery, ownership manifest, or automatic installation. Regenerate and install scripts through your project's build process when its CLI changes.
To generate this CLI's own Fish completion:
pixi run typer-static-completion generate typer_static_completion.cli:app --prog-name typer-static-completion --shell fish -o typer-static-completion.fish
The CLI is also available as pixi run python -m typer_static_completion.cli.
Interactive screen snapshots
Bash, Fish, and Zsh have real interactive PTY screen snapshots like those in
commander-static-completion, recording suggestions, inserted text, and cursor
position. The isolated snapshot environment provides all three shells, pexpect, and pyte on
Linux/macOS. Ordinary unit tests can run without those integration dependencies.
pixi run -e snapshots test-snapshots
pixi run -e snapshots update-snapshots
Review changes under tests/snapshots/ after updating. Each screen snapshot has
sections for all three shells. Full completion files sit alongside them in
tests/snapshots/generated/demo.{bash,fish,zsh}, making changes to the emitted
code and its size reviewable over time. Both kinds of snapshots use the same
update/check commands; full-script checks also run with ordinary unit tests. Missing or changed
snapshots fail checks; updating is forbidden in CI. The harness uses an isolated
80x24 terminal, named editing keys, timeouts, process cleanup, and sentinels that
fail if static completion invokes the CLI or Python. CI checks the snapshots on
Linux and macOS. Fish terminal capability negotiation is exercised by the harness.
The parsing matrix in tests/parsing_cases.py covers scalar/variadic arguments,
repeated options, count flags, short clusters, shadowed parent options, and --.
Its screen tests assert the expected completed line before comparing snapshots.
tests/snapshots/parsing/ also checks two CLIs loaded together and sourced twice;
tests/snapshots/generated/parsing.{bash,fish,zsh} records the corresponding full
completion files. Separate tests verify the tricky cases against Typer's parser.
The CLI itself has four shared interactive cases in tests/snapshots/cli/ and
full scripts in tests/snapshots/generated/cli.{bash,fish,zsh}.
The coverage fixture adds 45 shared screens for custom/disabled help flags,
hidden commands/options, deprecated commands, literal help descriptions, Unicode,
and escaped values, including metacharacters already in the typed prefix. These cases use C.UTF-8 and live in tests/snapshots/coverage/,
with full scripts in tests/snapshots/generated/coverage.{bash,fish,zsh}.
Completed lines are parsed by the actual shell using a controlled stub to verify
argument values and reject executable substitutions before snapshots can update.
Bash explicitly quotes literal candidates containing expansion syntax because
Readline's filename quoting alone can leave backticks executable. Custom help
aliases retain their configured order for deterministic output across processes.
Bash respects quotes and escapes when finding the part Readline will replace,
including prefixes with embedded quotes. Zsh avoids inserting a literal trailing
space inside an already-closed quoted value.
The tuple cases in tests/tuple_cases.py have interactive screen snapshots and a
full generated-script fixture alongside the existing parsing matrix.
The tuple-argument fixture adds 24 shared screens covering interspersed options,
--, following scalar/variadic arguments, choices, paths, and directories.
Full scripts live in tests/snapshots/generated/tuple-arguments.{bash,fish,zsh}.
The group-argument fixture adds 27 shared screens for parent arguments, nested
groups, option boundaries, --, paths, and optional/variadic arguments.
Full scripts live in tests/snapshots/generated/group-arguments.{bash,fish,zsh}.
The case-matching fixture adds 21 shared interactive screens for insensitive
options, arguments, tuple positions, ambiguous matches, and accented values,
plus sensitive-choice regressions. Full scripts live in
tests/snapshots/generated/case.{bash,fish,zsh}.
The Unicode fixture adds 14 shared screens for CJK characters, single-code-point
emoji, and decomposed/stacked accents, including completion in the middle of a
line. Cursor markers use terminal cells rather than string indices. Snapshots
also record the exact editor buffer and cursor offset, and tests pass the
completed arguments through Typer so screen normalization cannot hide changes
to decomposed values. Zsh's default display shows combining marks as codes;
the original code points remain in its edit buffer. Full scripts live in
tests/snapshots/generated/unicode.{bash,fish,zsh}.
The word-break fixture adds 12 Bash-specific snapshot cases, each checked with
six COMP_WORDBREAKS settings (72 scenarios). They cover :, =, and @,
quoted/escaped prefixes, assignments, and attached short options, with exact
editor-state checks. Bash preserves @ when Readline includes it in the word
being replaced. Full scripts live in
tests/snapshots/generated/word-breaks.{bash,fish,zsh}.
Current snapshot baselines target the locked Bash 5.x, Fish 4.x, and Zsh 5.9 environment. Multi-code-point emoji sequences, line wrapping, unusual shell parsing modes, and filenames containing control characters still need broader coverage.
Installation
This project is managed by pixi. You can install the package in development mode using:
git clone https://github.com/pavelzw/typer-static-completion
cd typer-static-completion
pixi run pre-commit-install
pixi run test
Metadata
Release files for typer-static-completion 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| typer_static_completion-0.0.2.tar.gz | 138.7 kB | With provenance | Details for typer_static_completion-0.0.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| typer_static_completion-0.0.2-py3-none-any.whl | Python 3 | none | any | Details for typer_static_completion-0.0.2-py3-none-any.whl |
Total release size: 172.5 kB
Release files / typer_static_completion-0.0.2.tar.gz
| Download URL | typer_static_completion-0.0.2.tar.gz |
|---|---|
| Size | 138.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b164f7ae41ea1a4452e3726ef957d0a0da69cdcb58b2ab0b64978207c0c8df92
|
|
BLAKE2b-256 checksum How to use checksums |
321e4835a843bbb2be2fcfb0bb0da77b42e97cb6cba4827b32f90040241cc400
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / typer_static_completion-0.0.2-py3-none-any.whl
| Download URL | typer_static_completion-0.0.2-py3-none-any.whl |
|---|---|
| Size | 33.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3ed365c1704000ccd7d2740811ec456790cb48592242a9fc9dc6ba01dca40682
|
|
BLAKE2b-256 checksum How to use checksums |
1553e9f29d52b187d44bfa7da164b1bf49da0aa5a93667ff610013f475c92cd7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logSecurity for typer-static-completion 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for typer_static_completion-0.0.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.