lib.Cmd: Add argument "parent" to __init__()

During __init__(), commands have no idea of their parent. This is not
a problem as of now, but is easy to fix, and it's architecturally
desirable to be prepared just in case, so add the parent argument to
the ctor before more commands are added.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-01-27 10:22:16 +01:00
commit f175f9d5c9
32 changed files with 103 additions and 88 deletions

View file

@ -65,7 +65,6 @@ class App: # export
self.__eloop = asyncio.get_event_loop()
self.__own_eloop = True
self.__cmds: list[Cmd] = []
self.__parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description=description, add_help=False)
self._add_arguments(self.__parser)
@ -78,7 +77,7 @@ class App: # export
cmd_classes = LoadTypes(modules if modules else ['__main__'], type_name_filter=name_filter, type_filter=[Cmd])
add_all_parsers = '-h' in sys.argv or '--help' in sys.argv
add_cmds_to_parser(self, self.__parser, [cmd_class() for cmd_class in cmd_classes], all=add_all_parsers)
add_cmds_to_parser(self, self.__parser, [cmd_class(self) for cmd_class in cmd_classes], all=add_all_parsers)
# -- Add help only now, wouldn't want to have parse_known_args() exit on --help with subcommands missing
self.__parser.add_argument('-h', '--help', action='help', help='Show this help message and exit')
@ -131,7 +130,7 @@ class App: # export
# Run sub-command. Overwrite if you want to do anything before or after
async def _run(self, args: argparse.Namespace) -> None:
return await self.__args.func(args)
return await self.args.func(args)
@property
def args(self) -> argparse.Namespace:

View file

@ -10,15 +10,12 @@ 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:
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.__parent: App|Cmd|None = None
self.__name = name
self.__help = help
self.__children: list[Cmd] = []
@ -37,7 +34,7 @@ class Cmd(abc.ABC): # export
return self.__parent
@property
def app(self):
def app(self) -> App:
from .App import App
if self.__app is None:
parent = self.__parent
@ -85,8 +82,7 @@ class Cmd(abc.ABC): # export
if cmd_class in self.__child_classes:
continue
self.__child_classes.append(cmd_class)
cmd = cmd_class()
cmd.set_parent(self)
cmd = cmd_class(self)
self.__children.append(cmd)
assert len(self.__children) == len(self.__child_classes)
except Exception as e:
@ -99,9 +95,3 @@ class Cmd(abc.ABC): # export
# 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