Metadata-Version: 2.4
Name: argcomplete-snapshot
Version: 0.1.0
Summary: Fast Bash completion artifacts for import-heavy argparse and argcomplete CLIs.
Project-URL: Homepage, https://github.com/zelnkup/argcomplete-snapshot
Project-URL: Repository, https://github.com/zelnkup/argcomplete-snapshot
Project-URL: Issues, https://github.com/zelnkup/argcomplete-snapshot/issues
Author: Illia Stebelskyi
License: MIT
License-File: LICENSE
Keywords: argcomplete,argparse,autocomplete,bash,cli,completion
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Terminals
Classifier: Typing :: Typed
Requires-Python: >=3.13
Provides-Extra: bench
Requires-Dist: argcomplete>=3.6.3; extra == 'bench'
Requires-Dist: matplotlib>=3.10.7; extra == 'bench'
Requires-Dist: requests>=2.32.5; extra == 'bench'
Requires-Dist: shtab>=1.8.0; extra == 'bench'
Provides-Extra: dev
Requires-Dist: mypy>=1.18.2; extra == 'dev'
Requires-Dist: pre-commit>=4.3.0; extra == 'dev'
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
Requires-Dist: pytest>=8.4.2; extra == 'dev'
Requires-Dist: ruff>=0.13.0; extra == 'dev'
Description-Content-Type: text/markdown

# argcomplete-snapshot

![PyPI](https://img.shields.io/pypi/v/argcomplete-snapshot)
![Coverage](https://img.shields.io/badge/coverage-92%25-brightgreen)

## 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:

```bash
pip install argcomplete-snapshot
```

Install benchmark extras from a checkout:

```bash
pip install ".[bench]"
```

## Usage

### 1. Build a lightweight parser

```python
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

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

### 3. Print the activation snippet

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

### 4. Add dynamic fallback only when needed

```bash
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

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

## Benchmarks

Results:

- [benchmarks/results/README.md](benchmarks/results/README.md)
- [benchmarks/results/benchmark-results.json](benchmarks/results/benchmark-results.json)
- [benchmarks/results/hot-path-comparison.png](benchmarks/results/hot-path-comparison.png)
- [benchmarks/results/spawned-comparison.png](benchmarks/results/spawned-comparison.png)

Hot path:

![Hot path comparison](benchmarks/results/hot-path-comparison.png)

Spawned shell:

![Spawned shell comparison](benchmarks/results/spawned-comparison.png)

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:

```bash
make benchmark
```

## Development

```bash
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
