Skip to main content

Xliner's CLI Framework

Project description

Xclif

uv Code style: black Ruff Imports: isort Checked with mypy codecov

CI PyPI - Python Version PyPI PyPI - License Read the Docs

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 / -v wired up automatically
  • Config managementWithConfig[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 compile pre-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 testingcommand.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 hyperfinebrew 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 Bryan and --name=Bryan
  • Boolean flags: --verbose sets to True
  • Short aliases: -v for --verbose (auto-generated)
  • -- separator for raw positional passthrough
  • Variadic positional args (*files: str)
  • str, int, float, bool as argument/option types
  • --help / -h on every command
  • --version on 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.toml finalized 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 in from_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 type statements, 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xclif-0.1.1.tar.gz (113.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xclif-0.1.1-py3-none-any.whl (32.7 kB view details)

Uploaded Python 3

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

Hashes for xclif-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1f8c5a269e64ebdde7cec3e43680ae756072ce4f6f3ec5fe4f08bf3bf9177585
MD5 91ee7b2d1dad0e14f13c632c2c2a0574
BLAKE2b-256 941ca38c8c3de78a0a0b506d8cf6d9dde31e2f7adc120d30fefb02bcf77b72f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for xclif-0.1.1.tar.gz:

Publisher: release.yml on ThatXliner/xclif

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for xclif-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9ac63572f84058555969eb95bb581cbe2f5207eaea9228aa19c25c93bed99b4a
MD5 3f2993d1f1912ffcc456295c23d9fdb9
BLAKE2b-256 710b55a076444eaec36cbef79f4d334975992653308a4b2f71251fff68f01ca3

See more details on using hashes here.

Provenance

The following attestation bundles were made for xclif-0.1.1-py3-none-any.whl:

Publisher: release.yml on ThatXliner/xclif

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page