pmb.helpers.run_core: add output=pipe (MR 1996)

This adds a new output mode "pipe" that is identical to the existing
"background" mode except for that its stdout is redirected into a
pipe so that it can be retrieved.
This commit is contained in:
Johannes Marbach 2020-11-27 20:37:26 +01:00 committed by Oliver Smith
parent 82d149c85d
commit 45dbeca587
No known key found for this signature in database
GPG Key ID: 5AE7F5513E0885CB
2 changed files with 40 additions and 10 deletions

View File

@ -20,7 +20,7 @@ def sanity_checks(output="log", output_return=False, check=None,
Raise an exception if the parameters passed to core() don't make sense
(all parameters are described in core() below).
"""
if output not in ["log", "stdout", "interactive", "tui", "background"]:
if output not in ["log", "stdout", "interactive", "tui", "background", "pipe"]:
raise RuntimeError("Invalid output value: " + str(output))
# Prevent setting the check parameter with output="background".
@ -45,6 +45,14 @@ def background(args, cmd, working_dir=None):
return ret
def pipe(args, cmd, working_dir=None):
""" Run a subprocess in background and redirect its output to a pipe. """
ret = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=args.logfd,
cwd=working_dir)
logging.verbose(f"New background process: pid={ret.pid}, output=pipe")
return ret
def pipe_read(args, process, output_to_stdout=False, output_return=False,
output_return_buffer=False):
"""
@ -215,14 +223,16 @@ def core(args, log_message, cmd, working_dir=None, output="log",
"stdout", "interactive", "background"), so it's easy to
trace what pmbootstrap does.
The exception is "tui" (text-based user interface), where
The exceptions are "tui" (text-based user interface), where
it does not make sense to write to the log file (think of
ncurses UIs, such as "menuconfig").
ncurses UIs, such as "menuconfig") and "pipe" where the
output is written to a pipe for manual asynchronous
consumption by the caller.
When the output is not set to "interactive", "tui" or
"background", we kill the process if it does not output
anything for 5 minutes (time can be set with "pmbootstrap
--timeout").
When the output is not set to "interactive", "tui",
"background" or "pipe", we kill the process if it does not
output anything for 5 minutes (time can be set with
"pmbootstrap --timeout").
The table below shows all possible values along with
their properties. "wait" indicates that we wait for the
@ -235,18 +245,20 @@ def core(args, log_message, cmd, working_dir=None, output="log",
"interactive" | | x | x | x
"tui" | | | x | x
"background" | | x | |
"pipe" | | | |
:param output_return: in addition to writing the program's output to the
destinations above in real time, write to a buffer
and return it as string when the command has
completed. This is not possible when output is
"background" or "tui".
"background", "pipe" or "tui".
:param check: an exception will be raised when the command's return code
is not 0. Set this to False to disable the check. This
parameter can not be used when the output is "background".
parameter can not be used when the output is "background" or
"pipe".
:param kill_as_root: use sudo to kill the process when it hits the timeout.
:returns: * program's return code (default)
* subprocess.Popen instance (output is "background")
* subprocess.Popen instance (output is "background" or "pipe")
* the program's entire output (output_return is True)
"""
sanity_checks(output, output_return, check, kill_as_root)
@ -259,6 +271,10 @@ def core(args, log_message, cmd, working_dir=None, output="log",
if output == "background":
return background(args, cmd, working_dir)
# Pipe
if output == "pipe":
return pipe(args, cmd, working_dir)
# Foreground
output_after_run = ""
if output == "tui":

View File

@ -56,6 +56,20 @@ def test_background(args):
assert process.poll() is None
def test_pipe(args):
# Sleep in background
process = pmb.helpers.run_core.pipe(args, ["sleep", "1"], "/")
# Check if it is still running
assert process.poll() is None
# Print output in background
process = pmb.helpers.run_core.pipe(args, ["echo", "-n", "hello"], "/")
# Read output
assert process.communicate()[0].decode('utf-8') == "hello"
def test_foreground_pipe(args):
func = pmb.helpers.run_core.foreground_pipe
cmd = ["echo", "test"]