lib.ExecContext: Align .sudo() prototype to .run()

ExecContext's .sudo() omits many of run()'s parameters, and this
commit adds them. To avoid redundancy around repeating and massaging
the long parameter list of both functions and their return values, it
also adds some deeper changes:

  - Make run(), _run(), sudo() and _sudo() always return instances of
    Result. Before it was allowed to return a triplet of stdout,
    stderr, and exit status.

  - Have ExecContext stay out of the business of decoding the result
    entirely. Result provides a convenience method .decode()
    operating on stdout and stderr and leaves the decision to the
    caller.

    This entails miniscule adaptations in calling code, namely in
    App.os_release, util.get_profile_env() and CmdListRepos._run().

  - Wrap the _run() and _sudo() callbacks in a context manager object
    of type CallContext to avoid code duplication.

  - Consistently name the first argument to run(), _run(), sudo() and
    _sudo() "cmd", not "args". The latter suggests that the caller is
    omitting the executable, which is not the case.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-03-19 11:38:16 +01:00
commit 02697af568
6 changed files with 165 additions and 100 deletions

View file

@ -139,9 +139,9 @@ async def get_profile_env(throw: bool=True, keep: Iterable[str]|bool=False, ec:
}
# Run bash as a login shell, which sources /etc/profile, then print environment as NUL-separated key=value pairs
cmd = ['/usr/bin/env', '-i', '/bin/bash', '-lc', 'env -0']
stdout, stderr, status = await run_cmd(cmd, throw=throw, output_encoding="bytes", verbose=True, env=env, ec=ec)
result = await run_cmd(cmd, throw=throw, verbose=True, env=env, ec=ec)
ret: dict[str, str] = {}
for entry in stdout.split(b"\0"):
for entry in result.stdout.split(b"\0"):
if not entry:
continue
key, val = entry.split(b"=", 1)