Intuitive, easy CLIs based on type hints.
—
Comprehensive exception hierarchy for precise error handling and debugging in CLI applications.
Root exception class for all Cyclopts errors.
class CycloptsError(Exception):
"""Base exception class for all Cyclopts errors."""
def __init__(self, message: str = ""):
"""
Create a Cyclopts exception.
Parameters
----------
message
Error message
"""Exceptions related to argument parsing and validation.
class ArgumentOrderError(CycloptsError):
"""Error when positional arguments are in wrong order."""
class MissingArgumentError(CycloptsError):
"""Error when required argument is missing."""
class MixedArgumentError(CycloptsError):
"""Error when incompatible argument types are mixed."""
class RepeatArgumentError(CycloptsError):
"""Error when an argument is repeated inappropriately."""
class UnknownOptionError(CycloptsError):
"""Error when an unknown option is provided."""
class UnusedCliTokensError(CycloptsError):
"""Error when CLI tokens are not consumed during parsing."""Exceptions related to type coercion and conversion.
class CoercionError(CycloptsError):
"""Error when type coercion fails."""
def __init__(
self,
message: str = "",
*,
type_: type | None = None,
value: Any = None,
tokens: list[Token] | None = None
):
"""
Create a coercion error.
Parameters
----------
message
Error message
type_
Target type that failed conversion
value
Value that failed conversion
tokens
Tokens that caused the error
"""Exceptions related to value validation.
class ValidationError(CycloptsError):
"""Error during value validation."""
def __init__(
self,
message: str = "",
*,
value: Any = None,
validator: Callable | None = None
):
"""
Create a validation error.
Parameters
----------
message
Error message
value
Value that failed validation
validator
Validator that rejected the value
"""Exceptions related to command configuration and option handling.
class CommandCollisionError(CycloptsError):
"""Error when command names collide."""
class InvalidCommandError(CycloptsError):
"""Error when a command is invalid or malformed."""
class CombinedShortOptionError(CycloptsError):
"""Error when combined short options are invalid."""Exceptions related to docstring parsing and help generation.
class DocstringError(CycloptsError):
"""Error parsing function docstrings."""
def __init__(
self,
message: str = "",
*,
func: Callable | None = None,
docstring: str | None = None
):
"""
Create a docstring error.
Parameters
----------
message
Error message
func
Function with problematic docstring
docstring
The problematic docstring content
"""Exceptions related to interactive text editing.
class EditorError(CycloptsError):
"""Base error for editor operations."""
class EditorNotFoundError(EditorError):
"""Error when text editor is not found."""
def __init__(self, editor: str | None = None):
"""
Create editor not found error.
Parameters
----------
editor
Editor command that was not found
"""
class EditorDidNotSaveError(EditorError):
"""Error when user did not save in editor."""
class EditorDidNotChangeError(EditorError):
"""Error when user did not change content in editor."""from cyclopts import App, run
from cyclopts import ValidationError, CoercionError, MissingArgumentError
app = App()
@app.command
def process_data(input_file: str, threshold: float):
"""Process data with error handling."""
print(f"Processing {input_file} with threshold {threshold}")
def main():
try:
app()
except ValidationError as e:
print(f"Validation failed: {e}")
return 1
except CoercionError as e:
print(f"Type conversion failed: {e}")
return 1
except MissingArgumentError as e:
print(f"Required argument missing: {e}")
return 1
except Exception as e:
print(f"Unexpected error: {e}")
return 1
return 0
if __name__ == "__main__":
exit(main())from cyclopts import App, CycloptsError
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = App()
@app.command
def risky_operation(count: int):
"""Operation that might fail."""
if count < 0:
raise ValueError("Count must be positive")
print(f"Processing {count} items")
def main():
try:
app()
except CycloptsError as e:
# Handle all Cyclopts-specific errors
logger.error(f"CLI error: {e}")
return 1
except ValueError as e:
# Handle application-specific errors
logger.error(f"Application error: {e}")
return 2
except KeyboardInterrupt:
logger.info("Operation cancelled by user")
return 130
return 0
if __name__ == "__main__":
exit(main())from cyclopts import App, edit
from cyclopts import EditorError, EditorNotFoundError, EditorDidNotSaveError
app = App()
@app.command
def edit_message():
"""Edit message with comprehensive error handling."""
try:
content = edit(
text="Enter your message here...",
require_save=True,
require_change=True
)
print("Message created successfully:")
print(content)
except EditorNotFoundError as e:
print(f"No editor available: {e}")
print("Please set the EDITOR environment variable")
return 1
except EditorDidNotSaveError:
print("Editor session cancelled - no changes saved")
return 1
except EditorError as e:
print(f"Editor error: {e}")
return 1
return 0from cyclopts import App, Parameter
from cyclopts.validators import Number
from cyclopts import ValidationError
def validate_even_number(value: int) -> int:
"""Validate that number is even."""
if value % 2 != 0:
raise ValidationError(f"Value {value} must be even", value=value)
return value
app = App()
@app.command
def process_batch(
batch_size: int = Parameter(
validator=[Number(min=1, max=1000), validate_even_number],
help="Batch size (1-1000, must be even)"
)
):
"""Process data in batches."""
print(f"Processing with batch size {batch_size}")
def main():
try:
app()
except ValidationError as e:
print(f"Invalid input: {e}")
if hasattr(e, 'value') and e.value is not None:
print(f"Problematic value: {e.value}")
return 1
return 0
if __name__ == "__main__":
exit(main())from cyclopts import App, CycloptsError
from cyclopts import (
ValidationError, CoercionError, MissingArgumentError,
CommandCollisionError, DocstringError
)
import sys
def handle_cyclopts_error(error: CycloptsError) -> int:
"""Handle Cyclopts errors with appropriate messages and exit codes."""
if isinstance(error, ValidationError):
print(f"❌ Validation Error: {error}", file=sys.stderr)
return 2
elif isinstance(error, CoercionError):
print(f"❌ Type Error: {error}", file=sys.stderr)
return 3
elif isinstance(error, MissingArgumentError):
print(f"❌ Missing Argument: {error}", file=sys.stderr)
return 4
elif isinstance(error, CommandCollisionError):
print(f"❌ Configuration Error: {error}", file=sys.stderr)
return 5
elif isinstance(error, DocstringError):
print(f"❌ Documentation Error: {error}", file=sys.stderr)
return 6
else:
print(f"❌ CLI Error: {error}", file=sys.stderr)
return 1
app = App()
@app.command
def example(value: int):
"""Example command."""
print(f"Value: {value}")
def main():
try:
app()
return 0
except CycloptsError as e:
return handle_cyclopts_error(e)
except KeyboardInterrupt:
print("\n⚠️ Operation cancelled", file=sys.stderr)
return 130
except Exception as e:
print(f"💥 Unexpected error: {e}", file=sys.stderr)
return 1
if __name__ == "__main__":
exit(main())Install with Tessl CLI
npx tessl i tessl/pypi-cyclopts