From 45dbeca587c02643ee99af1a6b185c81e11a7614 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Fri, 27 Nov 2020 20:37:26 +0100 Subject: [PATCH] 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. --- pmb/helpers/run_core.py | 36 ++++++++++++++++++++++++++---------- test/test_run_core.py | 14 ++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/pmb/helpers/run_core.py b/pmb/helpers/run_core.py index 290f5d01..55377024 100644 --- a/pmb/helpers/run_core.py +++ b/pmb/helpers/run_core.py @@ -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": diff --git a/test/test_run_core.py b/test/test_run_core.py index 62dea54e..7272589e 100644 --- a/test/test_run_core.py +++ b/test/test_run_core.py @@ -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"]