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
└── config/
├── __init__.py → myapp config
├── get.py → myapp config get
└── set.py → myapp config set
# 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) - 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 - ExtendedIO — reference arbitrary URLs, zip files, SSH, git repos, and other resources as inputs
- Automatic plugin discovery — 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.
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
ExtendedIO
ExtendedIO is Xclif's approach to transparent resource access. Instead of limiting CLI inputs to local file paths, ExtendedIO lets commands accept arbitrary URIs — URLs, zip archives, SSH paths, git repositories — and resolves them behind the scenes. The API uses dependency injection to provide a unified interface for accessing these resources.
Coming soon.
Roadmap
Milestone 1: 0.1.0 — Usable Core
- Option value parsing:
--name Bryanand--name=Bryan - Boolean flags:
--verbosesets toTrue - Short aliases:
-vfor--verbose(auto-generated) -
--separator for raw positional passthrough - Variadic positional args (
*files: str) -
str,int,float,boolas argument/option types -
--help/-hon every command -
--versionon root command (auto-detected from package metadata) - Rich-formatted help text with alignment
-
Cli.from_routes()stable and tested -
xclif compile+Cli.from_manifest()for zero-overhead startup -
list[str]for repeated options:--tag foo --tag bar - Proper error messages with friendly, formatted output (includes edit-distance suggestions)
-
pyproject.tomlfinalized and published to PyPI
Milestone 2: 0.2.0 — Developer Experience
-
WithConfig[T]— read from config files (TOML/JSON) and env vars (stub exists, not yet functional) -
Annotated[str, Arg(description="...")]for per-parameter metadata - Shell completion generation for bash, zsh, fish (stub exists)
- Distinct user errors vs developer errors with different output styles
- Documented exit codes
- Skip
_-prefixed modules infrom_routes(private helpers inside the routes package)
Milestone 3: 0.3.0 — Power Features
- Mutually exclusive option groups
- Cascading global options (opt-in per option)
- Pre/post command hooks (middleware for auth, logging, etc.)
- Plugin system: third-party type converters, custom implicit options
- ExtendedIO
Non-goals
- Database access or application-level business logic
- Supporting Python < 3.12 (we use
typestatements, generics syntax, etc.) - A GUI or TUI framework — Xclif is strictly for text CLIs
- Automatic retry, rate limiting, or async command execution
Contributing
See CONTRIBUTING.md.
License
This project is licensed under the GNU GPL v3+.
In short, this means you can do anything with it (distribute, modify, sell) but if you were to publish your changes, you must make the source code and build instructions readily available.
If you are a company using this project and want an exception, email thatxliner@gmail.com.
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.1.1.tar.gz.
File metadata
- Download URL: xclif-0.1.1.tar.gz
- Upload date:
- Size: 113.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f8c5a269e64ebdde7cec3e43680ae756072ce4f6f3ec5fe4f08bf3bf9177585
|
|
| MD5 |
91ee7b2d1dad0e14f13c632c2c2a0574
|
|
| BLAKE2b-256 |
941ca38c8c3de78a0a0b506d8cf6d9dde31e2f7adc120d30fefb02bcf77b72f7
|
Provenance
The following attestation bundles were made for xclif-0.1.1.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.1.1.tar.gz -
Subject digest:
1f8c5a269e64ebdde7cec3e43680ae756072ce4f6f3ec5fe4f08bf3bf9177585 - Sigstore transparency entry: 1236014847
- Sigstore integration time:
-
Permalink:
ThatXliner/xclif@f4d2ad3945c27f8244fabcbf5cfcf6143d39732a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ThatXliner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f4d2ad3945c27f8244fabcbf5cfcf6143d39732a -
Trigger Event:
release
-
Statement type:
File details
Details for the file xclif-0.1.1-py3-none-any.whl.
File metadata
- Download URL: xclif-0.1.1-py3-none-any.whl
- Upload date:
- Size: 32.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ac63572f84058555969eb95bb581cbe2f5207eaea9228aa19c25c93bed99b4a
|
|
| MD5 |
3f2993d1f1912ffcc456295c23d9fdb9
|
|
| BLAKE2b-256 |
710b55a076444eaec36cbef79f4d334975992653308a4b2f71251fff68f01ca3
|
Provenance
The following attestation bundles were made for xclif-0.1.1-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.1.1-py3-none-any.whl -
Subject digest:
9ac63572f84058555969eb95bb581cbe2f5207eaea9228aa19c25c93bed99b4a - Sigstore transparency entry: 1236014851
- Sigstore integration time:
-
Permalink:
ThatXliner/xclif@f4d2ad3945c27f8244fabcbf5cfcf6143d39732a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ThatXliner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f4d2ad3945c27f8244fabcbf5cfcf6143d39732a -
Trigger Event:
release
-
Statement type: