mirror of
ssh://git.janware.com/janware/proj/jw-pkg
synced 2026-04-28 10:15:23 +02:00
107 lines
3.2 KiB
Python
107 lines
3.2 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
|
||
|
|
|
||
|
|
# full blown example of one level of nested subcommands
|
||
|
|
# git -C project remote -v show -n myremote
|
||
|
|
|
||
|
|
class Cmd(abc.ABC): # export
|
||
|
|
|
||
|
|
def __init__(self, name: str, help: str) -> None:
|
||
|
|
from . import App
|
||
|
|
self.__app: App|None = None
|
||
|
|
self.__parent: App|Cmd|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):
|
||
|
|
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)
|
||
|
|
|
||
|
|
@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()
|
||
|
|
cmd.set_parent(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 ({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
|
||
|
|
|
||
|
|
def conf_value(self, path, default=None):
|
||
|
|
ret = None if self.app is None else self.app.conf_value(path, default)
|
||
|
|
if ret is None and default is not None:
|
||
|
|
return default
|
||
|
|
return ret
|