From 500ddd2a77958b2ec299eb9633dcc16ee60bad75 Mon Sep 17 00:00:00 2001 From: TheOnePath Date: Wed, 5 Jul 2023 18:35:00 +0100 Subject: [PATCH] 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`. --- soypak/cli/command.py | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 soypak/cli/command.py diff --git a/soypak/cli/command.py b/soypak/cli/command.py new file mode 100644 index 0000000..531241b --- /dev/null +++ b/soypak/cli/command.py @@ -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.")