From 670a46d0213380527c482f0be724cf6740cfb627 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Sat, 22 Aug 2026 11:27:19 +0200 Subject: [PATCH] lib.ExecApp: Add class Signed-off-by: Jan Lindemann --- src/python/jw/pkg/lib/ExecApp.py | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/python/jw/pkg/lib/ExecApp.py diff --git a/src/python/jw/pkg/lib/ExecApp.py b/src/python/jw/pkg/lib/ExecApp.py new file mode 100644 index 00000000..992b2cea --- /dev/null +++ b/src/python/jw/pkg/lib/ExecApp.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import sys + +from typing import TYPE_CHECKING, Any, override + +from .App import App as Base + +if TYPE_CHECKING: + + from argparse import ArgumentParser + + from .ExecContext import ExecContext + +class ExecApp(Base): # export + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.__opt_interactive: bool | None = None + self.__opt_verbose: bool | None = None + self.__exec_context: ExecContext | None = None + + @override + async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None: + if self.__exec_context is not None: + await self.__exec_context.close() + self.__exec_context = None + + @override + def _add_arguments(self, parser: ArgumentParser) -> None: + super()._add_arguments(parser) + parser.add_argument( + '--interactive', + choices = ['true', 'false', 'auto'], + default = 'true', + help = 'Wait for user input or try to proceed unattended', + ) + parser.add_argument( + '--verbose', + action = 'store_true', + default = False, + help = "Be verbose on stderr about what's being done on the distro level", + ) + parser.add_argument( + '--target', default = 'local', help = 'Run commands on this host' + ) + + @property + def interactive(self) -> bool: + if self.__opt_interactive is None: + match self.args.interactive: + case 'true': + self.__opt_interactive = True + case 'false': + self.__opt_interactive = False + case 'auto': + self.__opt_interactive = sys.stdin.isatty() + case _: + raise ValueError( + f'Unknown --interactive value: {self.args.interactive}' + ) + # Not logically possible to fail, but this keeps pyright happy + assert self.__opt_interactive is not None + return self.__opt_interactive + + @property + def verbose(self) -> bool: + if self.__opt_verbose is None: + self.__opt_verbose = self.args.verbose + # Not logically possible to fail, but this keeps pyright happy + assert self.__opt_verbose is not None + return self.__opt_verbose + + @property + def exec_context(self) -> ExecContext: + if self.__exec_context is None: + from .ExecContext import ExecContext + + self.__exec_context = ExecContext.create( + self.args.target, + interactive = self.interactive, + verbose_default = self.verbose, + ) + return self.__exec_context