Search code examples
pythonpython-3.xapicommand-line-interfaceauto-generate

auto built cli tool in to an object in python


first sorry for my bad terminology, I am an electrical engineer, so maybe my coding terms are not so accurate or even far from that.

we have a CLI in the company, accessed from the Linux terminal, you know usual stuff, `{command.exe} {plugin} {options}, and you get the output on the terminal screen.

In order to unit test the product, we need it in a python class, which is returned as an object to the test environment, and eventually, prints that open a process that execute that command.

to build the command, we have a dictionary of the plugin, the subplugin, and the option for each cmd:

        self.commands = {
            "plugin": ['subplugin', 'subsubplugin', '-a', 'flaga', '-b', 'flagb'],...

and we built a function for every command we want, from the plugin list extracted from the dict above

I am looking for a better approach that auto-built the tool entirely, sort of what the OS does for prediction.

I am assuming that would include the "set_attr" method of classes and stuff like that.

at the end of all this, I expect to access the plugin like this: cli.plugin.subplugin.subsubplugin(arg,arg,arg)
and that would generate a command cli, or at least the list above so I could inject it into the existing infra.

can anyone help, please?

thx in advance

I am more looking for guidence then say what I tried and fix it.


Solution

  • I found my answer, this code worked for me yo achieve what I was looking for. thanks for the commenters.

    import re
    import subprocess
    
    PKG_NAME = "sudo mycli"
    PKG_PLUGIN_START = "The following are all installed plugin extensions:" # this is the message before the commands list in the cli help
    PKG_PLUGIN_END = f"See 'mycli <plugin> help' for more information on a plugin" # hit is the message after the commands list in the cli help
    PKG_CMD_START = "The following are all implemented sub-commands:"
    PKG_CMD_END = "See 'mycli help <command>' for more information on a specific command"
    PLUGIN_CMD_START = PKG_CMD_START
    PLUGIN_CMD_END = "See 'mycli <plugin> help <command>' for more information on a specific command"
    
    
    def get_help(s):
        s += " help"
        return subprocess.getoutput([s])
    
    
    def get_plugin_list(s, start, end):
        s = '\n'.join(l.strip() for l in s.splitlines() if l)
        res = re.search(f'{start}([\s\S]*){end}', s)  # regex that matches everything between both strings
    
        if not res:
            raise ValueError("Couldn't find plugin list in string")
    
        return [l.split(' ')[0] for l in res.group(1).strip().splitlines()]  # remove the unnecessary text and return the plugins as a list
    
    
    class CMD():
        def __init__(self, name, parent_plugin_name=None, *args):
            self.args = args
            self.pkg_name = PKG_NAME
            self.parent_plugin_name = parent_plugin_name
            self.name = name
    
        def __call__(self, *args, **kwargs):
            if self.parent_plugin_name:
                command = " ".join([self.pkg_name, self.parent_plugin_name])
            else:
                command = self.pkg_name
            command = " ".join([command, self.name, *args, " "])
            command += " ".join([f"-{each[0]}={each[1]}" for each in list(kwargs.items())])
            return subprocess.getoutput(command)
    
    
    class Plugin():
    
        def __init__(self, name, parent_pkg_name):
            self.name = name
            self.parent_pkg_name = PKG_NAME
            plugin_cmd_start = PLUGIN_CMD_START
            plugin_cmd_end = PLUGIN_CMD_END.replace("<plugin>", self.name)
            for cmd in get_plugin_list(get_help(f"{self.parent_pkg_name} {self.name}"), plugin_cmd_start, plugin_cmd_end):
                setattr(self, cmd, CMD(cmd, parent_plugin_name=self.name))
    
    
    class Package():
        def __init__(self, name, root=True):
            self.name = name
            if root:
                self.name = "sudo " + self.name
            self.command_string = f"{self.name}"
            for cmd in get_plugin_list(get_help(self.name), PKG_CMD_START, PKG_CMD_END):
                setattr(self, cmd, CMD(cmd))
            for plugin in get_plugin_list(get_help(self.name), PKG_PLUGIN_START, PKG_PLUGIN_END):
                setattr(self, plugin, Plugin(plugin, parent_pkg_name=self.name))
    
    
    if __name__ == "__main__":
        mycli_tool = Package("mycli")
        print()
        print(mycli_tool.cmd())
        print()
        print(mycli_tool.system.get_disk_usage("-x0"))
        print()
        print(mycli_tool.system.get_disk_usage(x=0))
        print()
        print(mycli_tool.system.get_disk_usage(json=1))