Typing stubs for click - a command line interface creation kit for Python
Comprehensive exception hierarchy for handling CLI errors with proper error messages, exit codes, and user feedback. Includes parameter validation errors and usage errors.
Foundation exception classes that provide common error handling functionality.
class ClickException(Exception):
exit_code: int
message: str
def __init__(self, message: str) -> None:
"""
Base exception for all click-related errors.
Parameters:
- message: Error message
Attributes:
- exit_code: Exit code for the application (default: 1)
- message: Error message text
Usage:
raise click.ClickException('Something went wrong')
"""
def format_message(self) -> str:
"""
Format the exception message.
Returns:
Formatted error message
"""
def show(self, file: Any | None = None) -> None:
"""
Show the exception message to the user.
Parameters:
- file: File to write to (defaults to stderr)
"""Exceptions for command usage and parameter errors.
class UsageError(ClickException):
ctx: Context | None
cmd: Command | None
def __init__(self, message: str, ctx: Context | None = None) -> None:
"""
Exception for usage errors (wrong parameters, invalid commands, etc.).
Parameters:
- message: Error message
- ctx: Context where error occurred
Usage:
if len(args) == 0:
raise click.UsageError('At least one argument is required')
"""
def show(self, file: IO[Any] | None = None) -> None:
"""Show usage error with command usage information."""Specific exceptions for parameter validation and processing errors.
class BadParameter(UsageError):
param: Parameter | None
param_hint: str | None
def __init__(
self,
message: str,
ctx: Context | None = None,
param: Parameter | None = None,
param_hint: str | None = None,
) -> None:
"""
Exception for invalid parameter values.
Parameters:
- message: Error message
- ctx: Context where error occurred
- param: Parameter that caused the error
- param_hint: Hint about which parameter failed
Usage:
# Usually raised by parameter types
if not os.path.exists(path):
raise click.BadParameter(f'Path {path} does not exist')
"""
class MissingParameter(BadParameter):
param_type: str
def __init__(
self,
message: str | None = None,
ctx: Context | None = None,
param: Parameter | None = None,
param_hint: str | None = None,
param_type: str | None = None,
) -> None:
"""
Exception for missing required parameters.
Parameters:
- message: Error message
- ctx: Context where error occurred
- param: Missing parameter
- param_hint: Hint about which parameter is missing
- param_type: Type of parameter ('parameter', 'option', 'argument')
Usage:
# Usually raised automatically by click
if required_param is None:
raise click.MissingParameter('Required parameter missing')
"""Specific exceptions for option and argument processing errors.
class NoSuchOption(UsageError):
option_name: str
possibilities: list[str] | None
def __init__(
self,
option_name: str,
message: str | None = None,
possibilities: list[str] | None = None,
ctx: Context | None = None,
) -> None:
"""
Exception for unknown options.
Parameters:
- option_name: Name of unknown option
- message: Error message
- possibilities: List of similar option names
- ctx: Context where error occurred
Usage:
# Usually raised automatically by click
if option not in valid_options:
raise click.NoSuchOption(option, possibilities=['--help', '--version'])
"""
class BadOptionUsage(UsageError):
option_name: str
def __init__(self, option_name: str, message: str, ctx: Context | None = None) -> None:
"""
Exception for incorrect option usage.
Parameters:
- option_name: Name of the option
- message: Error message
- ctx: Context where error occurred
Usage:
# When option is used incorrectly
raise click.BadOptionUsage('--count', 'Option requires a value')
"""
class BadArgumentUsage(UsageError):
def __init__(self, message: str, ctx: Context | None = None) -> None:
"""
Exception for incorrect argument usage.
Parameters:
- message: Error message
- ctx: Context where error occurred
Usage:
# When arguments are used incorrectly
raise click.BadArgumentUsage('Too many arguments provided')
"""Exceptions for file-related operations.
class FileError(ClickException):
ui_filename: str
filename: str
def __init__(self, filename: str, hint: str | None = None) -> None:
"""
Exception for file operation errors.
Parameters:
- filename: Name of the file that caused the error
- hint: Additional hint about the error
Usage:
try:
with open(filename, 'r') as f:
content = f.read()
except IOError:
raise click.FileError(filename, 'Could not read file')
"""Exceptions used for controlling command execution flow.
class Abort(RuntimeError):
"""
Exception for aborting command execution.
Raised by Context.abort() and confirmation dialogs when user
chooses to abort.
Usage:
if not click.confirm('Continue with dangerous operation?'):
raise click.Abort()
# Or use context method
ctx.abort()
"""
class Exit(RuntimeError):
exit_code: int
def __init__(self, code: int = 0) -> None:
"""
Exception for exiting with specific code.
Parameters:
- code: Exit code
Usage:
if success:
raise click.Exit(0)
else:
raise click.Exit(1)
# Or use context method
ctx.exit(code)
"""Custom Validation with Exceptions:
@click.command()
@click.option('--port', type=int)
def start_server(port):
if port and (port < 1 or port > 65535):
raise click.BadParameter('Port must be between 1 and 65535',
param_hint='--port')
if port and port < 1024:
if not click.confirm(f'Port {port} requires root privileges. Continue?'):
raise click.Abort()Custom Parameter Type with Exceptions:
class PortType(click.ParamType):
name = 'port'
def convert(self, value, param, ctx):
if value is None:
return None
try:
port = int(value)
except ValueError:
self.fail(f'{value} is not a valid port number', param, ctx)
if not (1 <= port <= 65535):
self.fail(f'Port {port} is not in valid range 1-65535', param, ctx)
return port
@click.option('--port', type=PortType())
def connect(port):
click.echo(f'Connecting to port {port}')Error Handling with Context:
@click.command()
@click.argument('filename')
@click.pass_context
def process_file(ctx, filename):
try:
if not os.path.exists(filename):
ctx.fail(f'File {filename} does not exist')
with open(filename, 'r') as f:
data = f.read()
# Process data
result = process_data(data)
except PermissionError:
ctx.fail(f'Permission denied accessing {filename}')
except json.JSONDecodeError as e:
ctx.fail(f'Invalid JSON in {filename}: {e}')
except Exception as e:
if ctx.obj and ctx.obj.get('debug'):
raise # Re-raise in debug mode
else:
ctx.fail(f'Error processing {filename}: {e}')Graceful Error Messages:
@click.command()
@click.option('--config', type=click.Path(exists=True), required=True)
def deploy(config):
try:
with open(config, 'r') as f:
config_data = json.load(f)
except json.JSONDecodeError as e:
# Convert generic exception to click exception
raise click.BadParameter(
f'Configuration file contains invalid JSON: {e}',
param_hint='--config'
)
required_keys = ['host', 'port', 'database']
missing_keys = [key for key in required_keys if key not in config_data]
if missing_keys:
raise click.BadParameter(
f'Configuration missing required keys: {", ".join(missing_keys)}',
param_hint='--config'
)Custom Exception Classes:
class DeploymentError(click.ClickException):
def __init__(self, message, deployment_id=None):
super().__init__(message)
self.deployment_id = deployment_id
self.exit_code = 2 # Custom exit code
def format_message(self):
msg = super().format_message()
if self.deployment_id:
msg += f' (Deployment ID: {self.deployment_id})'
return msg
@click.command()
def deploy():
try:
deployment_id = start_deployment()
monitor_deployment(deployment_id)
except DeploymentFailure as e:
raise DeploymentError(str(e), deployment_id) from eInstall with Tessl CLI
npx tessl i tessl/pypi-types-click