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

@ -4,13 +4,14 @@ from __future__ import annotations
from argparse import ArgumentParser
from ..App import App
from ..lib.Cmd import Cmd as Base
from ..lib.Types import LoadTypes
class Cmd(Base): # export
def __init__(self, name: str, help: str) -> None:
super().__init__(name, help)
def __init__(self, parent: App|Base, name: str, help: str) -> None:
super().__init__(parent, name, help)
def _add_subcommands(self) -> None:
self.add_subcommands(

View file

@ -1,19 +1,19 @@
# -*- coding: utf-8 -*-
import sys, inspect, re
import sys
from argparse import ArgumentParser
from ..App import App
from .Cmd import Cmd as CmdBase
class CmdProjects(CmdBase): # export
def __init__(self) -> None:
super().__init__('projects', help='Project metadata evaluation for building packages')
def __init__(self, parent: App) -> None:
super().__init__(parent, 'projects', help='Project metadata evaluation for building packages')
self._add_subcommands()
def add_arguments(self, p: ArgumentParser) -> None:
super().add_arguments(p)
def _run(self, args):
breakpoint()
raise Exception("Running with args", args)

View file

@ -5,6 +5,7 @@ from argparse import Namespace, ArgumentParser
from ...lib.log import *
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class BaseCmdPkgRelations(Cmd):
@ -76,8 +77,8 @@ class BaseCmdPkgRelations(Cmd):
def print_pkg_relations(self, rel_type, args_):
print(self.pkg_relations(rel_type, args_))
def __init__(self, relation: str, help: str) -> None:
super().__init__('pkg-' + relation, help=help)
def __init__(self, parent: CmdProjects, relation: str, help: str) -> None:
super().__init__(parent, 'pkg-' + relation, help=help)
self.relation = relation
def add_arguments(self, parser: ArgumentParser) -> None:

View file

@ -6,12 +6,13 @@ from functools import lru_cache
from ...lib.log import *
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
from ...App import Scope
class CmdBuild(Cmd): # export
def __init__(self) -> None:
super().__init__('build', help='janware software project build tool')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'build', help='janware software project build tool')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,12 +3,13 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
from ...App import Scope
class CmdCflags(Cmd): # export
def __init__(self) -> None:
super().__init__('cflags', help='cflags')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'cflags', help='cflags')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -4,11 +4,12 @@ from argparse import Namespace, ArgumentParser
from ...lib.log import *
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdCheck(Cmd): # export
def __init__(self) -> None:
super().__init__('check', help='Check for circular dependencies between given modules')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'check', help='Check for circular dependencies between given modules')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdCommands(Cmd): # export
def __init__(self) -> None:
super().__init__('commands', help='List available commands')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'commands', help='List available commands')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -4,11 +4,12 @@ import textwrap
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdCreatePkgConfig(Cmd): # export
def __init__(self) -> None:
super().__init__('create-pkg-config', help='Generate a pkg-config file for a module')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'create-pkg-config', help='Generate a pkg-config file for a module')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdExepath(Cmd): # export
def __init__(self) -> None:
super().__init__('exepath', help='exepath')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'exepath', help='exepath')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -7,11 +7,12 @@ from urllib.parse import urlparse
from ...lib.log import *
from ...lib.util import run_cmd
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdGetAuthInfo(Cmd): # export
def __init__(self) -> None:
super().__init__('get-auth-info', help='Try to retrieve authentication information from the source tree')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'get-auth-info', help='Try to retrieve authentication information from the source tree')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdGetval(Cmd): # export
def __init__(self) -> None:
super().__init__('getval', help='Get value from project config')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'getval', help='Get value from project config')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdHtdocsDir(Cmd): # export
def __init__(self) -> None:
super().__init__('htdocs-dir', help='Print source directory containing document root of a given module')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'htdocs-dir', help='Print source directory containing document root of a given module')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdLdflags(Cmd): # export
def __init__(self) -> None:
super().__init__('ldflags', help='ldflags')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'ldflags', help='ldflags')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdLdlibpath(Cmd): # export
def __init__(self) -> None:
super().__init__('ldlibpath', help='ldlibpath')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'ldlibpath', help='ldlibpath')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdLibname(Cmd): # export
def __init__(self) -> None:
super().__init__('libname', help='libname')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'libname', help='libname')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,13 +3,14 @@
import re, os
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...lib.util import get_username, get_password, run_curl
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdListRepos(Cmd): # export
def __init__(self) -> None:
super().__init__('list-repos', help='Query a remote GIT server for repositories')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'list-repos', help='Query a remote GIT server for repositories')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -4,11 +4,12 @@ from argparse import Namespace, ArgumentParser
from ...lib.log import *
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdModules(Cmd): # export
def __init__(self) -> None:
super().__init__('modules', help='Query existing janware packages')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'modules', help='Query existing janware packages')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdOsCascade(Cmd): # export
def __init__(self) -> None:
super().__init__('os-cascade', help='Print project.conf\'s OS configuration precedence of machine this script runs on')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'os-cascade', help='Print project.conf\'s OS configuration precedence of machine this script runs on')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdPath(Cmd): # export
def __init__(self) -> None:
super().__init__('path', help='path')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'path', help='path')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -6,5 +6,5 @@ from .BaseCmdPkgRelations import BaseCmdPkgRelations as Base
class CmdPkgConflicts(Base): # export
def __init__(self) -> None:
super().__init__('conflicts', help='Print packages conflicting with a given package')
def __init__(self, parent: Base) -> None:
super().__init__(parent, 'conflicts', help='Print packages conflicting with a given package')

View file

@ -6,5 +6,5 @@ from .BaseCmdPkgRelations import BaseCmdPkgRelations as Base
class CmdPkgProvides(Base): # export
def __init__(self) -> None:
super().__init__('provides', help='Print packages and capabilities provided by a given package')
def __init__(self, parent: Base) -> None:
super().__init__(parent, 'provides', help='Print packages and capabilities provided by a given package')

View file

@ -6,5 +6,5 @@ from .BaseCmdPkgRelations import BaseCmdPkgRelations as Base
class CmdPkgRequires(Base): # export
def __init__(self) -> None:
super().__init__('requires', help='Print packages required for a given package')
def __init__(self, parent: Base) -> None:
super().__init__(parent, 'requires', help='Print packages required for a given package')

View file

@ -2,14 +2,15 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
# TODO: seems at least partly redundant to CmdPkgRequires / print_pkg_relations
class CmdPrereq(Cmd): # export
def __init__(self) -> None:
super().__init__('prereq', help='path')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'prereq', help='path')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -4,11 +4,12 @@ from argparse import Namespace, ArgumentParser
from ...lib.log import *
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdProjDir(Cmd): # export
def __init__(self) -> None:
super().__init__('proj-dir', help='Print directory of a given package')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'proj-dir', help='Print directory of a given package')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdPythonpath(Cmd): # export
def __init__(self) -> None:
super().__init__('pythonpath', help='Generate PYTHONPATH for given modules')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'pythonpath', help='Generate PYTHONPATH for given modules')
def add_arguments(self, p: ArgumentParser) -> None:
super().add_arguments(p)

View file

@ -2,13 +2,14 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ...App import Scope
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdPythonpathOrig(Cmd): # export
def __init__(self) -> None:
super().__init__('pythonpath_orig', help='pythonpath')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'pythonpath_orig', help='pythonpath')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -2,15 +2,16 @@
from argparse import Namespace, ArgumentParser
from ...App import Scope
from ...lib.log import *
from ..Cmd import Cmd
from ...App import Scope
from ..CmdProjects import CmdProjects
# TODO: seems at least partly redundant to CmdPkgRequires / print_pkg_relations
class CmdRequiredOsPkg(Cmd): # export
def __init__(self) -> None:
super().__init__('required-os-pkg', help='List distribution packages required for a package')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'required-os-pkg', help='List distribution packages required for a package')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdSummary(Cmd): # export
def __init__(self) -> None:
super().__init__('summary', help='Print summary description of given modules')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'summary', help='Print summary description of given modules')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdTest(Cmd): # export
def __init__(self) -> None:
super().__init__('test', help='Test')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'test', help='Test')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -3,11 +3,12 @@
from argparse import Namespace, ArgumentParser
from ..Cmd import Cmd
from ..CmdProjects import CmdProjects
class CmdTmplDir(Cmd): # export
def __init__(self) -> None:
super().__init__('tmpl-dir', help='Print directory containing templates of a given module')
def __init__(self, parent: CmdProjects) -> None:
super().__init__(parent, 'tmpl-dir', help='Print directory containing templates of a given module')
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

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