Source code for mini_arcade.cli.base_command
"""
Base command module
This module defines the BaseCommand class, which serves as a base for all
command implementations.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple
from mini_arcade.cli.base_command_processor import BaseCommandProcessor
from .argument_type import ArgumentType
from .exceptions import CommandException
from .registry import CommandRegistry
[docs]
class BaseCommand(ABC):
"""
Base class for all commands.
Registration is done via the implementation decorator:
@CommandRegistry.implementation("build")
class Build(BaseCommand): ...
or:
from .command_registry import CommandRegistry
@CommandRegistry.implementation("build")
class Build(BaseCommand): ...
Subclasses should implement the execute(...) method as the main entrypoint.
:ivar name: Optional[str]: Command name (for registry); defaults to class name lowercased.
:ivar aliases: Tuple[str, ...]: Optional command aliases.
:ivar summary: Optional[str]: Short description of the command.
:ivar epilog: Optional[str]: Additional help text for the command.
:ivar args: Optional[List[ArgumentType]]: List of command arguments.
:ivar abstract: bool: If True, the command is not registered (base class
for shared logic); defaults to False.
:ivar processor: Optional[BaseCommandProcessor]: The processor associated with this command.
"""
# Metadata read by CommandRegistry.implementation(...)
name: Optional[str] = None
aliases: Tuple[str, ...] = ()
summary: Optional[str] = None
epilog: Optional[str] = None
args: Optional[List[ArgumentType]] = None
abstract: bool = False # if True, decorator will skip registration
processor: Optional[BaseCommandProcessor] = None
# Keep __init_subclass__ empty to avoid import/registration cycles
[docs]
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
[docs]
@classmethod
def define_arguments(cls) -> List[ArgumentType]:
"""
Return the list of ArgumentType for this command.
:return: List of ArgumentType instances.
:rtype: List[ArgumentType]
"""
return list(cls.args or [])
[docs]
def validate(self, **_kwargs):
"""Optional argument validation hook."""
# override in subclasses
return None
[docs]
def set_processor(self, processor: BaseCommandProcessor):
"""
Set the processor for the command.
:param processor: The processor for the command.
:type processor: BaseCommandProcessor
"""
if not issubclass(processor, BaseCommandProcessor):
raise CommandException(
f"Processor {processor} is not a subclass of BaseCommandProcessor"
)
self.processor = processor
def _run(self, **kwargs):
"""Internal run (pre-exec)."""
if not self.processor:
raise CommandException("Processor must be set")
processor_instance: BaseCommandProcessor = self.processor(**kwargs)
return processor_instance.run()
@abstractmethod
def _execute(self, **kwargs):
"""Internal execution step (core logic)."""
[docs]
def execute(self, **kwargs):
"""External command entrypoint."""
return self._execute(**kwargs)
# Bind the implementation_base now that BaseCommand exists (avoids circular import issues)
CommandRegistry.implementation_base = BaseCommand