tux.cli.core
¶
Core CLI functionality for Tux.
This module provides the main Click command group and utilities for the CLI.
Classes:
Name | Description |
---|---|
GlobalOptionGroup | |
Functions:
Name | Description |
---|---|
run_command | Run a command and return its exit code. |
cli | Tux CLI |
command_registration_decorator | Universal command decorator for registering commands on any group. |
create_group | Create a new command group and register it with the main CLI. |
register_commands | Load and register all CLI commands. |
main | Entry point for the CLI. |
start | Start the Discord bot |
Classes¶
GlobalOptionGroup
¶
Bases: Group
Methods:
Name | Description |
---|---|
parse_args | Parses arguments, extracting global --dev/--prod flags first. |
Functions¶
parse_args(ctx: Context, args: list[str]) -> list[str]
¶
Parses arguments, extracting global --dev/--prod flags first.
Stores the determined environment mode in ctx.meta['is_dev']. Removes the flags from the args list before standard parsing.
Source code in tux/cli/core.py
def parse_args(self, ctx: Context, args: list[str]) -> list[str]:
"""
Parses arguments, extracting global --dev/--prod flags first.
Stores the determined environment mode in ctx.meta['is_dev'].
Removes the flags from the args list before standard parsing.
"""
is_dev = True # Default to development mode
remaining_args: list[str] = []
args_iterator = iter(args)
for arg in args_iterator:
if arg == "--dev":
is_dev = True # Explicitly set, though already default
elif arg == "--prod":
is_dev = False
else:
remaining_args.append(arg)
# Store the determined mode in the context metadata
ctx.meta["is_dev"] = is_dev
# Call the default parser with the modified arguments
return super().parse_args(ctx, remaining_args)
Functions¶
run_command(cmd: list[str], **kwargs: Any) -> int
¶
Run a command and return its exit code.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
cmd | list[str] | Command to run as a list of strings | required |
**kwargs | Any | Additional arguments to pass to subprocess.run | {} |
Returns:
Type | Description |
---|---|
int | Exit code of the command (0 for success) |
Source code in tux/cli/core.py
def run_command(cmd: list[str], **kwargs: Any) -> int:
"""Run a command and return its exit code.
Parameters
----------
cmd : list[str]
Command to run as a list of strings
**kwargs : Any
Additional arguments to pass to subprocess.run
Returns
-------
int
Exit code of the command (0 for success)
"""
try:
subprocess.run(cmd, check=True, **kwargs)
except subprocess.CalledProcessError as e:
return e.returncode
else:
return 0
cli(ctx: Context) -> None
¶
Tux CLI
Source code in tux/cli/core.py
@click.group(cls=GlobalOptionGroup)
@click.version_option(version=__version__, prog_name="Tux") # type: ignore[misc]
@click.pass_context
def cli(ctx: Context) -> None: # Remove env_dev and env_prod params
"""Tux CLI"""
# Initialize context object
ctx.ensure_object(dict) # Still useful for subcommands if they use ctx.obj
ctx.meta.setdefault("is_dev", True) # Ensure 'is_dev' exists even if parse_args wasn't fully run (e.g., --help)
# Retrieve the environment mode set by GlobalOptionGroup.parse_args
is_dev = ctx.meta["is_dev"]
configure_environment(dev_mode=is_dev)
# Conditionally set DATABASE_URL for commands that require it
invoked_command = ctx.invoked_subcommand
if invoked_command is not None and invoked_command not in NO_DB_COMMANDS:
logger.trace(f"Command '{invoked_command}' may require database access. Setting DATABASE_URL.")
try:
db_url = get_database_url()
os.environ["DATABASE_URL"] = db_url
logger.trace("Set DATABASE_URL environment variable for Prisma.")
except Exception as e:
# Log critical error and exit if URL couldn't be determined for a required command.
logger.critical(f"Command '{invoked_command}' requires a database, but failed to configure URL: {e}")
logger.critical("Ensure DEV_DATABASE_URL or PROD_DATABASE_URL is set in your .env file or environment.")
sys.exit(1) # Exit with a non-zero status code
elif invoked_command:
logger.trace(f"Command '{invoked_command}' does not require database access. Skipping DATABASE_URL setup.")
command_registration_decorator(target_group: Group, *args: Any, **kwargs: Any) -> Callable[[CommandFunction], Command]
¶
Universal command decorator for registering commands on any group.
Handles UI output and error handling. Environment is configured globally. Extracts params for the original function from ctx.params.
Source code in tux/cli/core.py
def command_registration_decorator(
target_group: Group,
*args: Any,
**kwargs: Any,
) -> Callable[[CommandFunction], Command]:
"""
Universal command decorator for registering commands on any group.
Handles UI output and error handling.
Environment is configured globally.
Extracts params for the original function from ctx.params.
"""
def decorator(func: CommandFunction) -> Command:
# Define the wrapper that will be registered as the command
# Remove dev/prod options here
@click.pass_context
def wrapper(ctx: Context, **kwargs: Any):
# This wrapper receives ctx and all original func params via kwargs
# Environment is assumed to be set by the global cli options.
# Get group and command names for output using context, ensuring non-None
group_name = (ctx.parent.command.name or "cli") if ctx.parent and ctx.parent.command else "cli"
cmd_name = (ctx.command.name or "unknown") if ctx.command else "unknown"
# Echo environment mode and command info
command_header(group_name, cmd_name)
# Display env info unconditionally now, as it's globally set
info(f"Running in {get_current_env()} mode")
# Execute the original command function
try:
# Pass all kwargs received directly to the original function
result = func(**kwargs)
success = result == 0
command_result(success)
# Return the actual result from the function
return result # noqa: TRY300
except Exception as e:
error(f"Command failed: {e!s}")
logger.exception("An error occurred during command execution.")
command_result(False)
return 1
# Update wrapper metadata from original function
wrapper = update_wrapper(wrapper, func)
# Register the wrapper function with the target group
return target_group.command(*args, **kwargs)(wrapper)
return decorator
create_group(name: str, help_text: str) -> Group
¶
Create a new command group and register it with the main CLI.
Source code in tux/cli/core.py
register_commands() -> None
¶
Load and register all CLI commands.
Source code in tux/cli/core.py
main() -> int
¶
Entry point for the CLI.
Source code in tux/cli/core.py
def main() -> int:
"""Entry point for the CLI."""
# Configure logging first!
setup_logging()
# No need for default env config here, handled by @cli options
# register_commands()
# Run the CLI
# Click will parse global options, call cli func, then subcommand func
# We need to ensure commands are registered before cli() is called.
register_commands()
return cli() or 0 # Return 0 if cli() returns None