mirror of
ssh://git.janware.com/janware/proj/jw-pkg
synced 2026-04-24 17:23:36 +02:00
Don't log an Exception as {e} but as str(e) producing nicer output.
Or as repr(e) if a backtrace is requested, because to people who can
read backtraces, type info might be of interest. Also, remove
pointless time stamps, those belong into the logging framework.
Signed-off-by: Jan Lindemann <jan@janware.com>
100 lines
3 KiB
Python
100 lines
3 KiB
Python
# -*- 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
|