# -*- coding: utf-8 -*- from __future__ import annotations from typing import Any import inspect, sys, re, abc, argparse from argparse import ArgumentParser, _SubParsersAction from .log import * from .Types import Types class Cmd(abc.ABC): # export def __init__(self, parent: App|Cmd, name: str, help: str) -> None: from . import App self.__parent: App|Cmd|None = parent self.__app: App|None = None self.__name = name self.__help = help self.__children: list[Cmd] = [] self.__child_classes: list[type[Cmd]] = [] async def _run(self, args): pass def set_parent(self, parent: Any|Cmd): self.__parent = parent @property def parent(self) -> App|Cmd: if self.__parent is None: raise Exception(f'Tried to access inexistent parent of command {self.name}') return self.__parent @property def app(self) -> App: from .App import App if self.__app is None: parent = self.__parent while True: if parent is None: raise Exception("Can't get application object from command without parent") if isinstance(parent, App): self.__app = parent break assert parent != parent.__parent parent = parent.__parent return self.__app @property def name(self) -> str: return self.__name @property def help(self) -> str: return self.__help @property def children(self) -> list[Cmd]: return tuple(self.__children) @property def child_classes(self) -> list[type[Cmd]]: return tuple(self.__child_classes) async def run(self, args): return await self._run(args) @abc.abstractmethod async def _run(self, args): pass def add_subcommands(self, cmds: Cmd|list[Cmds]|Types|list[Types]) -> None: if isinstance(cmds, Cmd): assert False return if isinstance(cmds, list): for cmd in cmds: self.add_subcommands(cmd) return if isinstance(cmds, Types): try: for cmd_class in cmds: if cmd_class in self.__child_classes: continue self.__child_classes.append(cmd_class) cmd = cmd_class(self) self.__children.append(cmd) assert len(self.__children) == len(self.__child_classes) except Exception as e: cmds.dump(ERR, f"Failed to add subcommands ({str(e)})") raise return raise Exception(f'Tried to add sub-commands of unknown type {type(cmds)}') # To be overridden by derived class in case the command does take arguments. # Will be called from App base class constructor and set up the parser hierarchy def add_arguments(self, parser: ArgumentParser) -> None: pass