From a739eb0763d3c56608a9e9974422e48d1133c5fe Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Mon, 15 Jun 2026 07:30:17 +0200 Subject: [PATCH] lib.Result: Initialize with status = None Define default parameter values for Result's constructor, namely None for exit status, stdout and stderr. Instantiating a Result object without parameters signifies "this object doesn't contain data from a real process's exit event". Up to now, similar meaning has been hand-crafted by ExecContext's run() and friends by using an error exit status (1) to make sure it wasn't mistaken for success. This commit formalizes that into the Result structure itself, but uses None instead for the exit status. Controlling default values in Result itself also means that the Result class gets better awareness of what it contains, and its log messages and stdin / stdout can be more fitting: - If a real process failed, make stdout return at least b'' - If a real process succeeded, make stdout return at least b'' Returning something from .stdout on success fixes a real bug: An attempt to access what "rpm -U somepackage.rpm" returns, namely nothing, raises a bogus exception, because stdout is None. Signed-off-by: Jan Lindemann --- src/python/jw/pkg/lib/ExecContext.py | 8 ++++---- src/python/jw/pkg/lib/Result.py | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/python/jw/pkg/lib/ExecContext.py b/src/python/jw/pkg/lib/ExecContext.py index d354cf13..398a5d9a 100644 --- a/src/python/jw/pkg/lib/ExecContext.py +++ b/src/python/jw/pkg/lib/ExecContext.py @@ -313,7 +313,7 @@ class ExecContext(Base): await self.open() try: - ret = Result(None, None, 1) + ret = Result() with self.CallContext( self, title = title, @@ -421,7 +421,7 @@ class ExecContext(Base): # be returned by CallContext and is very much allowed assert cmd_input is not None, 'Invalid: cmd_input is None' - ret = Result(None, None, 1) + ret = Result() with self.CallContext( self, title = title, @@ -454,7 +454,7 @@ class ExecContext(Base): async def _get( self, path: str, wd: str | None, throw: bool, verbose: bool | None, title: str ) -> Result: - ret = Result(None, None, 1) + ret = Result() if wd is not None: path = wd + '/' + path with self.CallContext( @@ -508,7 +508,7 @@ class ExecContext(Base): ) -> Result: return await self.run(cmd, cmd_input = cmd_input, **kwargs) - ret = Result(None, None, 1) + ret = Result() try: class RemoteCmd(NamedTuple): diff --git a/src/python/jw/pkg/lib/Result.py b/src/python/jw/pkg/lib/Result.py index d7353739..7a72ff59 100644 --- a/src/python/jw/pkg/lib/Result.py +++ b/src/python/jw/pkg/lib/Result.py @@ -4,9 +4,9 @@ class Result: def __init__( self, - stdout: bytes | None, - stderr: bytes | None, - status: int, + stdout: bytes | None = None, + stderr: bytes | None = None, + status: int | None = None, # Command has not yet exited encoding: str = 'UTF-8', strip: bool = True, cmd: list[str] | None = None, @@ -56,10 +56,11 @@ class Result: def __repr__(self) -> str: ret = f'{self.__status}:' - if self.status != 0: - ret += f' err: {self.stderr_str_or_none}' - else: - ret += f' out: {self.__stdout_footprint(quote=True)}' + if self.__status is not None: + if self.status != 0: + ret += f' err: {self.stderr_str_or_none}' + else: + ret += f' out: {self.__stdout_footprint(quote=True)}' return ret @property @@ -104,7 +105,10 @@ class Result: if wd is None: wd = self.__wd call = f'"{pretty_cmd(cmd, wd)}" ' - ret = f'Command {call}has exited with status {self.__status}' + ret = ( + f'Command {call}has not yet exited' if self.__status is None else + f'Command {call}has exited with status {self.__status}' + ) call = pretty_cmd(cmd, wd) if self.status != 0: ret += f' -> stderr="{self.__stderr!r}"' @@ -125,6 +129,8 @@ class Result: @property def stdout(self) -> bytes: if self.__stdout is None: + if self.__status == 0: + return b'' raise Exception(f'Result has no standard output stream: {self.summary}') return self.__stdout @@ -143,6 +149,8 @@ class Result: @property def stderr(self) -> bytes: if self.__stderr is None: + if isinstance(self.__status, int) and self.__status != 0: + return b'' raise Exception(f'Result has no standard error stream: {self.summary}') return self.__stderr -- 2.54.0