Thin wrapper around the Python 'click' library to allow building class based CLIs
Project description
ultraclick
A powerful class-based wrapper for building elegant command-line interfaces in Python.
ultraclick is a Python library that extends the popular click framework, enabling you to create structured CLI applications using object-oriented principles. It integrates rich-click for beautiful color formatting and enhanced readability.
Table of Contents
Features
ultraclick offers significant advantages over function-based CLI frameworks:
- Class-based CLI structure: Define your command-line interface using Python classes
- Nested command groups: Organize commands in a hierarchical structure
- Class instance state: Share data between commands naturally via instance attributes
- Context Proxy: Access the Click context directly with
click.ctxwithout passing it as a parameter - Context sharing: Share context data between different command levels via
ctx.meta - Rich output formatting: Colored output and better help text formatting via rich-click
- Interactive command execution: Preserves colors, progress bars, and interactive output from subprocesses
- Cross-platform compatibility: Works on Unix, macOS, and Windows
- Command aliases: Create alternative names for commands (e.g.,
infoas an alias forstatusoraddas an alias forcreate) - Command abbreviations: Type partial commands like
conf sinstead ofconfig showwhen unambiguous - Automatic return value handling: Command return values are automatically displayed
- Clean, maintainable code: Group related commands together with proper encapsulation
- Full compatibility: Supports all features of Click and RichClick
Installation
From PyPI (recommended)
pip install ultraclick
From Source
# Clone the repository
git clone https://github.com/ronny-rentner/ultraclick.git
cd ultraclick
# Create and activate a virtual environment (recommended)
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install the package in development mode (will install dependencies automatically)
pip install -e .
Demo Application
The repository includes a comprehensive demo that showcases ultraclick's features. After installation and activating your virtual environment, try the following commands:
./demo.py --help
./demo.py --profile production --verbose status
./demo.py config
./demo.py resource
./demo.py resource --resource-type database create mydb --size large --region eu-west
./demo.py config set debug true
./demo.py config update debug false
./demo.py r l
./demo.py --profile staging r --resource-type storage l
Tab Completion
Enable tab completion for the demo application in Bash:
eval "$(_DEMO_PY_COMPLETE=bash_source python ./demo.py)"
Now you can use Tab to complete commands, options, and arguments.
Help Output
Sample help outputs from the demo application:
Main Help
Usage: demo [OPTIONS] COMMAND [ARGS]...
Main demo application showcasing ultraclick's features.
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --verbose Enable verbose output │
│ --profile TEXT Configuration profile to use │
│ --env [development|staging|productio Environment to run in │
│ n] │
│ --version Show the version and exit. │
│ --help Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ config Configuration commands for the application. Demonstrates context │
│ sharing between subcommands. │
│ info Show application status. │
│ resource Resource management commands. Demonstrates parameter handling and │
│ nested command structure. │
│ status Show application status. │
╰──────────────────────────────────────────────────────────────────────────────╯
Config Command Help
Usage: demo config [OPTIONS] COMMAND [ARGS]...
Configuration commands for the application. Demonstrates context sharing
between subcommands.
This command group shows help when called directly (default behavior).
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --config-dir PATH Configuration directory │
│ --help Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ get Get a configuration value. │
│ set Set a configuration value. │
│ show Display current configuration settings. │
╰──────────────────────────────────────────────────────────────────────────────╯
Demo Code
Check out gwctl for a real-world application built with ultraclick.
#!/usr/bin/env python
"""
ultraclick Demo Application
This demo systematically showcases ultraclick's main features:
1. Class-based CLI structure
2. Nested command groups
3. Automatic context sharing
4. Command aliases
5. Command abbreviations
6. Automatic return value handling
"""
import pathlib
import ultraclick as click
class ConfigCommand:
"""
Configuration commands for the application.
Demonstrates context sharing between subcommands.
This command group shows help when called directly (default behavior).
"""
@click.option("--config-dir", type=pathlib.Path, default="./config", help="Configuration directory")
def __init__(self, config_dir):
# Store parameters as instance variables
self.config_dir = config_dir
# Access shared data from parent command
self.profile = click.ctx.meta["profile"]
# Share this command's data with child commands
click.ctx.meta["config_dir"] = config_dir
@click.command()
def show(self):
"""Display current configuration settings."""
path_prefix = './' if not self.config_dir.is_absolute() else ''
return (
f"Active Profile: {self.profile}\n"
f"Config Directory: {path_prefix}{self.config_dir}"
)
@click.command()
@click.argument("name")
@click.argument("value")
def set(self, name, value):
"""Set a configuration value."""
return f"Setting {name}={value} in profile '{self.profile}'"
# Command alias demonstration - using decorator approach
update = click.alias(set)
@click.command()
@click.argument("name")
def get(self, name):
"""Get a configuration value."""
return f"Getting '{name}' from profile '{self.profile}'"
# Another command alias - using decorator approach
fetch = click.alias(get)
class ResourceCommand:
"""
Resource management commands.
Demonstrates parameter handling and nested command structure.
This command group performs an action when called directly (no help shown).
"""
@click.option("--resource-type", type=click.Choice(['server', 'database', 'storage']),
default="server", help="Type of resource to manage")
def __init__(self, resource_type):
self.resource_type = resource_type
# Access shared data from parent command
self.profile = click.ctx.meta["profile"]
# Custom behavior when no subcommand is provided
if click.ctx.invoked_subcommand is None:
# Prevent automatic help display
click.ctx.meta['show_help_on_no_command'] = False
# Show custom summary instead
click.echo(
f"Resource Management Summary:\n"
f"• Current Resource Type: {self.resource_type}\n"
f"• Available Types: server, database, storage\n"
f"• Active Profile: {self.profile}"
)
@click.command()
@click.argument("name")
@click.option("--size", default="medium", help="Resource size (small, medium, large)")
@click.option("--region", default="us-east", help="Deployment region")
def create(self, name, size, region):
"""Create a new resource."""
return (
f"Creating {self.resource_type} '{name}'\n"
f"Size: {size}\n"
f"Region: {region}\n"
f"Using profile: {self.profile}"
)
@click.command()
@click.argument("name")
def delete(self, name):
"""Delete a resource."""
return f"Deleting {self.resource_type} '{name}'"
@click.command()
@click.argument("names", nargs=-1)
def list(self, names):
"""List resources, optionally filtered by name."""
if not names:
return f"Listing all {self.resource_type}s (Profile: {self.profile})"
else:
return f"Filtered {self.resource_type} list: {', '.join(names)} (Profile: {self.profile})"
class MainApp:
"""
Main demo application showcasing ultraclick's features.
"""
# Define nested command groups
config = ConfigCommand
resource = ResourceCommand
@click.option("--verbose", is_flag=True, help="Enable verbose output")
@click.option("--profile", default="default", help="Configuration profile to use")
@click.option("--env", default="development",
type=click.Choice(['development', 'staging', 'production']),
help="Environment to run in")
@click.version_option(version="1.0")
def __init__(self, verbose, profile, env):
# Store options as instance variables
self.verbose = verbose
self.profile = profile
self.env = env
# Share data with child commands
click.ctx.meta["verbose"] = verbose
click.ctx.meta["profile"] = profile
click.ctx.meta["env"] = env
if verbose:
click.output.success(f"Verbose mode enabled in {env} environment")
@click.command()
def status(self):
"""Show application status."""
return (
f"Status: Running\n"
f"Environment: {self.env}\n"
f"Verbose: {self.verbose}\n"
f"Profile: {self.profile}"
)
# Command alias using the decorator approach
info=click.alias(status)
if __name__ == "__main__":
click.group_from_class(MainApp, name="demo")()
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 ultraclick-0.1.1.tar.gz.
File metadata
- Download URL: ultraclick-0.1.1.tar.gz
- Upload date:
- Size: 32.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
260a1cf7ec40628d07f88f46b9c35c5a1ee43efe185fd17ddecd635018533cb7
|
|
| MD5 |
8c0819dac5e3871d393038d1105995cd
|
|
| BLAKE2b-256 |
28145d07521093a2b4909df1d229eb9120ccedf1e979ffd49064b892792d6e76
|
Provenance
The following attestation bundles were made for ultraclick-0.1.1.tar.gz:
Publisher:
release.yml on ronny-rentner/ultraclick
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ultraclick-0.1.1.tar.gz -
Subject digest:
260a1cf7ec40628d07f88f46b9c35c5a1ee43efe185fd17ddecd635018533cb7 - Sigstore transparency entry: 198509015
- Sigstore integration time:
-
Permalink:
ronny-rentner/ultraclick@2d115465dc625027843d9a51f485c8f30c0e6adb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ronny-rentner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2d115465dc625027843d9a51f485c8f30c0e6adb -
Trigger Event:
push
-
Statement type:
File details
Details for the file ultraclick-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ultraclick-0.1.1-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d4c14087d74d9f82924212cc2d9974425a6f37876cbedfa817cea1ec0b57f05
|
|
| MD5 |
f7e438699a1907adc7c2c88acd674b82
|
|
| BLAKE2b-256 |
31c523263e08131b07dc8788149924622cb7af56c1144610b3198a2f9d4fdefe
|
Provenance
The following attestation bundles were made for ultraclick-0.1.1-py3-none-any.whl:
Publisher:
release.yml on ronny-rentner/ultraclick
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ultraclick-0.1.1-py3-none-any.whl -
Subject digest:
0d4c14087d74d9f82924212cc2d9974425a6f37876cbedfa817cea1ec0b57f05 - Sigstore transparency entry: 198509026
- Sigstore integration time:
-
Permalink:
ronny-rentner/ultraclick@2d115465dc625027843d9a51f485c8f30c0e6adb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/ronny-rentner
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2d115465dc625027843d9a51f485c8f30c0e6adb -
Trigger Event:
push
-
Statement type: