Added command.py

Module is responsible for allowing the creation, registering and
initilisation of soypak commands.

Added class@RegisterCommand:
	- used by command classes as a metaclass. Will be auto-invoked upon
	  the class' declaration.
	- checks if a command is registered already with an existing name.
	- uses the class' `__doc__` attribute to register an entry on the
	  help display message with name and summary.

Added class@Command:
	- Inherited by command classes.
	- Keeps record of the registered commands and the instance of the
	  class which can then be invoked during `get_command()`.
	- Command@get_command (static method): find and return an
	  initialised instance of a command that has been registered.
	  Otherwise return `None`.
	- Command@run (virtual): to be implemented on a child class of
	  `Command`.
This commit is contained in:
Ethan Smith-Coss 2023-07-05 18:35:00 +01:00
parent 5700eea0ed
commit 500ddd2a77
Signed by: TheOnePath
GPG Key ID: 4E7D436CE1A0BAF1

58
soypak/cli/command.py Normal file
View File

@ -0,0 +1,58 @@
import argparse
import sys
import soypak.cli.parser as parser
class RegisterCommand(type):
"""Register a new command to soypak and add help information to the parser.
This class is to be used as metaclass for commands.
"""
def __init__(cls, name, bases, _dict):
super().__init__(name, bases, _dict)
# get the name of the command
name = getattr(cls, "__cmd_name__", None)
if name is None:
raise Exception("Command has no registered name.")
# check if the command already exists (means we have duplicate registered name)
if name in Command.registered_cmds:
raise Exception("Duplicate command, already exists.")
# add the command to the registered list, with `name` and class object
Command.registered_cmds[name] = cls
# fetch the documentation for the command
_help = cls.__doc__
if not _help:
raise Exception("Command does not provide a __doc__ attribute for usage.")
# add the command to the argparse._action_groups list (will appear as a positional in help message)
parser.SoypakParser.add_action_command(cmd=name, help=_help.split('\n')[0])
class Command:
"""Class for creating a new type of command. Keep record of the registered commands for soypak."""
# store all registered commands. Those are commands which soypak implements
registered_cmds: dict[str, RegisterCommand] = {}
def __init__(self) -> None:
# get/create the parser instance
self.parser = parser.SoypakParser()
@staticmethod
def get_command(name, *, args: list[str]) -> RegisterCommand | None:
"""Fetch and initialise a registered command, or return `None` is command cannot be found."""
return (
Command.registered_cmds[name](data=parser.SoypakParser().namespace, args=args) if name in
Command.registered_cmds else None
)
def die(self):
"""System exit with status code 0"""
sys.exit(0)
def run(self):
"""Virtual method which must be overridden by a new implementation in the child class."""
raise RuntimeError("Virtual method must be overridden by child class.")