Intuitive, easy CLIs based on type hints.
—
Extended functionality including interactive editing, token manipulation, custom dispatching, and help system customization.
Launch external text editors for interactive input.
def edit(
text: str = "",
editor: str | None = None,
suffix: str = ".txt",
require_save: bool = True,
require_change: bool = False
) -> str:
"""
Launch text editor for interactive editing.
Parameters
----------
text
Initial text content for editor
editor
Editor command to use. If None, uses $EDITOR environment variable
suffix
File suffix for temporary file
require_save
Whether user must save the file
require_change
Whether user must change the content
Returns
-------
str
Edited text content
Raises
------
EditorNotFoundError
If no editor is available
EditorDidNotSaveError
If user didn't save and require_save=True
EditorDidNotChangeError
If user didn't change content and require_change=True
"""Utilities for working with command-line tokens and parsing.
def env_var_split(value: str, type_: type) -> list[str]:
"""
Split environment variable value based on target type.
Parameters
----------
value
Environment variable string value
type_
Target type that determines splitting behavior
Returns
-------
list[str]
List of string tokens for conversion
"""
def default_name_transform(name: str) -> str:
"""
Convert Python identifier to CLI token format.
Parameters
----------
name
Python identifier (e.g., "my_parameter")
Returns
-------
str
CLI token format (e.g., "--my-parameter")
"""
UNSET: object
"""Sentinel value indicating no data provided."""Rich panel creation for custom help and error display.
def CycloptsPanel(
content: str,
*,
title: str | None = None,
border_style: str = "red",
padding: tuple[int, int] = (0, 1),
**kwargs
) -> Panel:
"""
Create Rich panel for Cyclopts help and error display.
Parameters
----------
content
Panel content text
title
Panel title
border_style
Rich border style name
padding
Panel padding (vertical, horizontal)
**kwargs
Additional Rich Panel parameters
Returns
-------
Panel
Rich Panel object for display
"""Protocol for implementing custom command dispatching logic.
class Dispatcher:
def __call__(
self,
command: Callable[..., Any],
bound: inspect.BoundArguments,
ignored: dict[str, Any],
/
) -> Any:
"""
Custom dispatcher protocol for command execution.
Parameters
----------
command
Function to dispatch/execute
bound
Bound arguments from parsing
ignored
Ignored/unused arguments
Returns
-------
Any
Result of function execution
"""Extended configuration options for App customization.
class CycloptsConfig:
def __init__(
self,
*,
console: Console | None = None,
show: bool = True,
group: Group | None = None,
name: str | tuple[str, ...] | None = None,
help: str | None = None,
negative: str | Iterable[str] | None = None,
converter: Callable | None = None,
validator: Callable | list[Callable] | None = None,
env_var: str | Iterable[str] | None = None,
**kwargs
):
"""
Advanced configuration options for Cyclopts components.
Parameters
----------
console
Rich console for output
show
Whether to show in help
group
Grouping configuration
name
Name override
help
Help text
negative
Negative flag names
converter
Custom converter function
validator
Validation functions
env_var
Environment variable names
"""Start interactive shell for applications.
def interactive_shell(app: App) -> None:
"""
Start interactive shell mode for an application.
Parameters
----------
app
App instance to run in interactive mode
"""Flexible version display and management.
def version_print(app: App) -> None:
"""
Print application version information.
Parameters
----------
app
App instance with version information
"""
def resolve_version_format(version: str | Callable[[], str]) -> str:
"""
Resolve version string from string or callable.
Parameters
----------
version
Version string or callable that returns version
Returns
-------
str
Resolved version string
"""from cyclopts import App, edit
app = App()
@app.command
def create_message():
"""Create a message using text editor."""
content = edit(
text="# Enter your message here\n",
suffix=".md",
require_change=True
)
print("Message created:")
print(content)
@app.command
def edit_config():
"""Edit configuration interactively."""
current_config = '{"timeout": 30, "retries": 3}'
new_config = edit(
text=current_config,
suffix=".json",
require_save=True
)
print("Updated configuration:")
print(new_config)from cyclopts import App
from cyclopts.protocols import Dispatcher
import asyncio
import inspect
class AsyncDispatcher:
"""Custom dispatcher that handles async functions."""
def __call__(self, func, bound_arguments, app):
if inspect.iscoroutinefunction(func):
return asyncio.run(func(*bound_arguments.args, **bound_arguments.kwargs))
else:
return func(*bound_arguments.args, **bound_arguments.kwargs)
app = App(dispatcher=AsyncDispatcher())
@app.command
async def async_task(duration: int = 5):
"""Run an async task."""
print(f"Starting async task for {duration} seconds...")
await asyncio.sleep(duration)
print("Async task completed!")from cyclopts import App, CycloptsPanel, Group
from rich.panel import Panel
# Custom group with panel styling
custom_group = Group(
"advanced",
"Advanced Options",
"Advanced configuration and debugging options",
panel=Panel.fit("Advanced Settings", border_style="blue")
)
app = App(group_parameters=custom_group)
@app.command
def deploy(
service: str,
debug: bool = False,
dry_run: bool = False
):
"""Deploy service with custom help styling."""
if debug:
print("Debug mode enabled")
if dry_run:
print("Dry run mode - no actual deployment")
print(f"Deploying {service}")from cyclopts import App
app = App(name="myshell")
@app.command
def status():
"""Show system status."""
print("System is running")
@app.command
def restart():
"""Restart the system."""
print("Restarting...")
@app.command
def shell():
"""Start interactive shell."""
app.interactive_shell()
if __name__ == "__main__":
app()from cyclopts import App, env_var_split
import os
app = App()
@app.command
def process_files():
"""Process files from environment variable."""
file_list_env = os.getenv("FILE_LIST", "")
if file_list_env:
files = env_var_split(file_list_env, list[str])
print(f"Processing {len(files)} files:")
for file in files:
print(f" - {file}")
else:
print("No files specified in FILE_LIST environment variable")
# Usage: FILE_LIST="file1.txt,file2.txt,file3.txt" python script.py process-filesfrom cyclopts import App
def custom_name_transform(name: str) -> str:
"""Transform Python names to UPPERCASE CLI names."""
return f"--{name.upper().replace('_', '-')}"
app = App(name_transform=custom_name_transform)
@app.command
def process_data(
input_file: str,
max_workers: int = 4,
enable_caching: bool = False
):
"""Process data with custom name transformation."""
# CLI will have --INPUT-FILE, --MAX-WORKERS, --ENABLE-CACHING
print(f"Processing {input_file} with {max_workers} workers")
if enable_caching:
print("Caching enabled")Install with Tessl CLI
npx tessl i tessl/pypi-cyclopts