Xliner's CLI Framework
Project description
Xclif
Xliner's CLI Framework
Read the Manifesto to understand why Xclif exists and how it compares to Click, Typer, and argparse.
Installation
pip install xclif
Or with uv:
uv add xclif
Quick Start
Your directory structure is your command tree:
myapp/
├── __init__.py
├── __main__.py
└── routes/
├── __init__.py → myapp
├── greet.py → myapp greet
└── server/
├── __init__.py → myapp server
├── start.py → myapp server start
└── stop.py → myapp server stop
# routes/greet.py
from xclif import command
@command()
def _(name: str, template: str = "Hello, {}!") -> None:
"""Greet someone by name."""
print(template.format(name))
# __main__.py
from xclif import Cli
from . import routes
cli = Cli.from_routes(routes)
if __name__ == "__main__":
cli()
No default → positional argument. Has default → --template option. Docstring → help text. Drop a file in the right folder and the command exists.
Features
- File-based routing — directory structure is the command tree
- Decorator + type-hint API — function signatures define the CLI contract
- Rich integration — beautiful help pages, formatted errors, progress indicators
- Built-in logging and verbosity —
--verbose/-vwired up automatically - Config management —
WithConfig[T]reads from config files or environment variables (CLI flag > env var > config file > default) - Agent-optimized help — auto-detects non-TTY output and emits a compact, token-efficient format for LLM agents and scripts
- Autogenerated shell completions — bash, zsh, fish
- Minimal overhead — custom parser built from scratch for fast startup;
xclif compilepre-builds a static manifest to eliminate route-walking cost - Plugin discovery (planned) — third-party subcommands via entry points (like Git or cargo)
- Easy testing —
command.execute(["greet", "Alice"])with explicit arg lists, no mocking needed
Performance
Performance is not a primary focus of Xclif. If startup latency is a hard constraint, Python is probably the wrong tool for the job. These numbers are here for fun. That being said, we do recommend switching to a manifest-based setup for large codebases.
Startup time
Benchmarked on macOS (Apple Silicon, Python 3.12, 30 iterations + 3 warmup, wall-clock subprocess time):
| Scenario | Click | Typer | Xclif (from_routes) |
Xclif (flat) | Xclif (manifest) |
|---|---|---|---|---|---|
greet World |
28.0 | 39.8 | 41.0 | 27.2 | 26.9 ✅ |
greet + options |
27.9 | 37.8 | 40.9 | 26.3 ✅ | 26.7 |
config set |
27.6 | 38.0 | 40.5 | 26.1 ✅ | 26.9 |
config get |
27.9 | 38.1 | 40.3 | 26.2 ✅ | 26.8 |
--help |
29.2 ✅ | 82.2 | 59.6 | 46.6 | 47.2 |
greet --help |
29.7 ✅ | 83.8 | 59.3 | 46.8 | 47.6 |
Xclif (flat) uses the decorator API (Command.command() / Command.group()) instead of from_routes, and is the fastest framework for command execution — edging out Click by ~1–2 ms. The --help gap (~20 ms vs Click) is Rich's lazy-import cost. If better scaling is desirable for large codebases, the manifest compiler pre-builds a static manifest that matches flat API performance without requiring manual command registration.
Typer is the slowest overall: it wraps Click with extra overhead, and its Rich-based help rendering adds ~52–53 ms on --help scenarios.
from_routes adds ~13–15 ms for the package walker on top of Xclif (flat) — the trade-off for zero-registration file-based routing.
from_manifest eliminates that cost. Running xclif compile myapp.routes once generates a _xclif_manifest.py next to your routes package; loading it with Cli.from_manifest() skips the filesystem walk entirely, matching flat API performance.
# __main__.py — manifest variant
from xclif import Cli
from myapp import _xclif_manifest
cli = Cli.from_manifest(_xclif_manifest)
if __name__ == "__main__":
cli()
# regenerate after adding or removing routes
xclif compile myapp.routes
To reproduce (requires hyperfine — brew install hyperfine on macOS):
bash benchmarks/bench_frameworks.sh
Parse and dispatch latency
Measured in-process (no subprocess, no import cost) on the same machine, 5 000 iterations:
| Scenario | Click | Typer | Xclif |
|---|---|---|---|
greet World |
72 µs | 496 µs | 2.7 µs ✅ |
greet + options |
77 µs | 490 µs | 4.0 µs ✅ |
config set |
82 µs | 578 µs | 3.4 µs ✅ |
config get |
79 µs | 553 µs | 3.2 µs ✅ |
--help |
131 µs ✅ | 1 879 µs | 483 µs |
greet --help |
142 µs ✅ | 2 281 µs | 553 µs |
Xclif's custom parser is ~25× faster than Click and ~170× faster than Typer for command dispatch. The --help cases are slower than Click because Rich is doing real formatting work; Typer is dramatically slower there due to its reflective help generation.
uv run python benchmarks/bench_parsing.py
Contributing
See CONTRIBUTING.md.
License
This project is licensed under the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xclif-0.4.0.tar.gz.
File metadata
- Download URL: xclif-0.4.0.tar.gz
- Upload date:
- Size: 126.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
243d66dd6c786d168c323b3a8f688aff31d65d9551cbd3a95b4d54a00060f0ad
|
|
| MD5 |
d11fc97f1ee474a91d7063db5981179b
|
|
| BLAKE2b-256 |
ad6898546f3fe5642d195ad075ee1fe2fc79861c137cf1921fa40c3586c31cb9
|
Provenance
The following attestation bundles were made for xclif-0.4.0.tar.gz:
Publisher:
release.yml on ThatXliner/xclif
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xclif-0.4.0.tar.gz -
Subject digest:
243d66dd6c786d168c323b3a8f688aff31d65d9551cbd3a95b4d54a00060f0ad - Sigstore transparency entry: 1259547934
- Sigstore integration time:
-
Permalink:
ThatXliner/xclif@e126c6d88899492caf84fa98bb03b2a135f32e6d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ThatXliner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e126c6d88899492caf84fa98bb03b2a135f32e6d -
Trigger Event:
release
-
Statement type:
File details
Details for the file xclif-0.4.0-py3-none-any.whl.
File metadata
- Download URL: xclif-0.4.0-py3-none-any.whl
- Upload date:
- Size: 32.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
996ee3527e4827d56e20bb26afd81551a2e051836fa108bbb07c0a6e16143a3b
|
|
| MD5 |
6818ab43822f5cae73a04c56b402f058
|
|
| BLAKE2b-256 |
bbff8dc10d9851cc4343589e4c084a11676f6ab959082e05269aff7f6864ad3f
|
Provenance
The following attestation bundles were made for xclif-0.4.0-py3-none-any.whl:
Publisher:
release.yml on ThatXliner/xclif
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xclif-0.4.0-py3-none-any.whl -
Subject digest:
996ee3527e4827d56e20bb26afd81551a2e051836fa108bbb07c0a6e16143a3b - Sigstore transparency entry: 1259548055
- Sigstore integration time:
-
Permalink:
ThatXliner/xclif@e126c6d88899492caf84fa98bb03b2a135f32e6d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ThatXliner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e126c6d88899492caf84fa98bb03b2a135f32e6d -
Trigger Event:
release
-
Statement type: