Skip to content

zelnkup/argcomplete-snapshot

Repository files navigation

argcomplete-snapshot

pip docs Coverage

What

Generate fast Bash completion artifacts for import-heavy Python CLIs built with argparse and argcomplete.

Why

Default argcomplete runs the target Python CLI on every completion request. For SDK-backed CLIs that means paying for:

  • Python startup
  • heavy imports
  • parser construction
  • completion logic

That can easily turn <TAB> into a 200-600+ ms operation.

How

argcomplete-snapshot extracts a lightweight completion model once and writes shell-native cache artifacts:

  • snapshot-v1.json
  • completion-v1.bash
  • version-stamp

Steady-state completion stays in Bash. Python only runs when:

  • artifacts are missing
  • the CLI changed after upgrade
  • a maintainer explicitly routes a dynamic resolver through a fallback command

Why Not Just Use Something Else?

  • argcomplete: good dynamic behavior, but slow because it starts Python on every completion
  • shtab: fast for static completion scripts, but not built around lazy self-refresh after pip install -U ... or explicit dynamic fallback contracts

This package is for the middle ground:

  • your CLI is too heavy for default argcomplete
  • you still need more lifecycle support than a pure static script

Requirements

  • Python 3.13+
  • Bash 4+

Bash 4+ is required because the generated runtime uses associative arrays.

Dynamic resolvers require an explicit fallback command. They are not inferred automatically.

Install

Install from PyPI:

pip install argcomplete-snapshot

Install benchmark extras from a checkout:

pip install ".[bench]"

Usage

1. Build a lightweight parser

import argparse

from argcomplete_snapshot import CompletionKind, set_completion


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="mycli")
    parser.add_argument("--output", choices=["json", "yaml", "text"])

    profile = parser.add_argument("--profile")
    set_completion(profile, resolver="config_profiles")

    path = parser.add_argument("--path")
    set_completion(path, kind=CompletionKind.FILE)

    return parser

2. Generate artifacts

acs-refresh refresh \
  --factory mypackage.cli:build_parser \
  --cli-name mycli \
  --distribution mycli

3. Print the activation snippet

acs-refresh print-activation \
  --factory mypackage.cli:build_parser \
  --cli-name mycli \
  --distribution mycli

4. Add dynamic fallback only when needed

acs-refresh print-activation \
  --factory mypackage.cli:build_parser \
  --cli-name mycli \
  --distribution mycli \
  --fallback-command "mycli --resolver-fallback"

Use fallback only for genuinely dynamic completions. Static options and choices should stay on the Bash hot path.

Cache Layout

~/.cache/argcomplete-snapshot/<cli-name>/
  snapshot-v1.json
  completion-v1.bash
  version-stamp

Benchmarks

Results:

Hot path:

Hot path comparison

Spawned shell:

Spawned shell comparison

Current published medians:

Case argcomplete snapshot shtab
Static hot path 491.10 ms 1.77 ms 23.46 ms
Static spawned shell 647.65 ms 165.43 ms 95.86 ms

Read those numbers carefully:

  • hot path is the relevant steady-state completion metric
  • spawned shell includes shell startup and sourcing overhead
  • dynamic fallback cases are slower because they intentionally call runtime logic

Benchmark environment for the published results:

  • macOS 26.3.1
  • MacBook Pro MacBookPro17,1
  • 8 CPU cores (4 performance + 4 efficiency)
  • 16 GB memory
  • Python 3.13.12 under uv
  • benchmark fixtures simulate heavy CLIs by importing requests and sleeping for 200 ms
  • benchmark strategy uses 5 spawned-shell samples and 15 hot-path samples per case and implementation

Run locally:

make benchmark

Development

pip install -e ".[dev,bench]"
make check
make benchmark

Status

Early release.

Implemented:

  • typed snapshot model
  • argparse extraction
  • Bash artifact emission
  • lazy cache refresh
  • activation snippet generation
  • explicit dynamic fallback contract
  • benchmark fixtures and comparisons

Packages

 
 
 

Contributors