diff --git a/src/python/jw/pkg/lib/ExecContext.py b/src/python/jw/pkg/lib/ExecContext.py index 0fab4a89..dd6a62fc 100644 --- a/src/python/jw/pkg/lib/ExecContext.py +++ b/src/python/jw/pkg/lib/ExecContext.py @@ -37,7 +37,6 @@ class ExecContext(abc.ABC): cmd_input: str|None, wd: str|None, log_prefix: str, - interactive: bool|None, throw: bool, verbose: bool ) -> None: @@ -47,13 +46,29 @@ class ExecContext(abc.ABC): delim_len = 120 self.__delim += '-' * max(0, delim_len - len(self.__delim)) self.__cmd = cmd - self.__cmd_input = cmd_input self.__wd = wd self.__log_prefix = log_prefix - self.__interactive = interactive if interactive is not None else ( - cmd_input == "mode:interactive" - or (cmd_input == "mode:auto" and sys.stdin.isatty()) - ) + + # -- At the end of this dance, interactive needs to be either True + # or False + interactive: bool|None = None + match cmd_input: + case 'mode:interactive': + interactive = True + case 'mode:non-interactive': + interactive = False + case 'mode:opt-interactive': + interactive = parent.interactive + case 'mode:auto': + interactive = sys.stdin.isatty() + if interactive is None: + interactive = parent.interactive + if interactive is None: + interactive = sys.stdin.isatty() + assert interactive in [ True, False ] + self.__interactive = interactive + + self.__cmd_input = cmd_input if cmd_input[:5] != 'mode:' else None self.__throw = throw self.__verbose = verbose if verbose is not None else parent.verbose_default self.__pretty_cmd: str|None = None @@ -116,7 +131,7 @@ class ExecContext(abc.ABC): raise e return result - def __init__(self, uri: str, interactive: bool=True, verbose_default=False): + def __init__(self, uri: str, interactive: bool|None=None, verbose_default=False): self.__uri = uri self.__interactive = interactive self.__verbose_default = verbose_default @@ -127,7 +142,7 @@ class ExecContext(abc.ABC): return self.__uri @property - def interactive(self) -> bool: + def interactive(self) -> bool|None: return self.__interactive @property @@ -144,7 +159,7 @@ class ExecContext(abc.ABC): wd: str|None = None, throw: bool = True, verbose: bool|None = None, - cmd_input: str|None = None, + cmd_input: str|None = 'mode:opt-interactive', env: dict[str, str]|None = None, title: str=None ) -> Result: @@ -157,10 +172,12 @@ class ExecContext(abc.ABC): throw: Raise an exception on non-zero exit status if True verbose: Emit log output while the command runs cmd_input: - - None -> stdin from /dev/null - - "mode:interactive" -> Inherit terminal stdin - - "mode:auto" -> Inherit terminal stdin if it is a TTY - - otherwise -> String fed to stdin + - "mode:opt-interactive" -> Let --interactive govern how to handle interactivity (default) + - "mode:interactive" -> Inherit terminal stdin + - "mode:auto" -> Inherit terminal stdin if it is a TTY + - "mode:non-interactive" -> stdin from /dev/null + - None -> Alias for mode:non-interactive + - otherwise -> Feed cmd_input to stdin env: The environment the command should be run in Returns: @@ -170,7 +187,7 @@ class ExecContext(abc.ABC): ret = Result(None, None, 1) with self.CallContext(self, title=title, cmd=cmd, cmd_input=cmd_input, wd=wd, - log_prefix='|', interactive=None, throw=throw, verbose=verbose) as cc: + log_prefix='|', throw=throw, verbose=verbose) as cc: try: ret = await self._run( cmd=cmd, @@ -205,7 +222,7 @@ class ExecContext(abc.ABC): if mod_env is None: mod_env = {} with self.CallContext(self, title=title, cmd=cmd, cmd_input=cmd_input, wd=wd, - log_prefix='|', interactive=None, throw=throw, verbose=verbose) as cc: + log_prefix='|', throw=throw, verbose=verbose) as cc: try: ret = await self._sudo( cmd=cmd,