From 1d1ba7811dfa2a952e1770101e967fada38f5be6 Mon Sep 17 00:00:00 2001 From: Jonathan Paynter Date: Mon, 10 Aug 2020 17:40:04 +0100 Subject: [PATCH] utils/misc: separate check_output functionality The custom check_output function consisted of two main parts: fetching the subprocess required for the command, and checking its output. It is convenient to provide functions that implement these parts distinctly, so that the output of any subprocess can be checked easily and the creation of a typical Popen object wrapped inside get_subprocess. --- devlib/utils/misc.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/devlib/utils/misc.py b/devlib/utils/misc.py index fdbc8b5..38ce21a 100644 --- a/devlib/utils/misc.py +++ b/devlib/utils/misc.py @@ -155,9 +155,22 @@ check_output_logger = logging.getLogger('check_output') check_output_lock = threading.Lock() -def check_output(command, timeout=None, ignore=None, inputtext=None, **kwargs): - """This is a version of subprocess.check_output that adds a timeout parameter to kill - the subprocess if it does not return within the specified time.""" +def get_subprocess(command, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + with check_output_lock: + process = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + preexec_fn=preexec_function, + **kwargs) + return process + + +def check_subprocess_output(process, timeout=None, ignore=None, inputtext=None): + output = None + error = None # pylint: disable=too-many-branches if ignore is None: ignore = [] @@ -166,19 +179,7 @@ def check_output(command, timeout=None, ignore=None, inputtext=None, **kwargs): elif not isinstance(ignore, list) and ignore != 'all': message = 'Invalid value for ignore parameter: "{}"; must be an int or a list' raise ValueError(message.format(ignore)) - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - with check_output_lock: - process = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - preexec_fn=preexec_function, - **kwargs) - - output = None - error = None try: output, error = process.communicate(inputtext, timeout=timeout) except subprocess.TimeoutExpired as e: @@ -191,14 +192,22 @@ def check_output(command, timeout=None, ignore=None, inputtext=None, **kwargs): error = error.decode(sys.stderr.encoding or 'utf-8', "replace") if output else '' if timeout_expired: - raise TimeoutError(command, output='\n'.join([output, error])) + raise TimeoutError(process.args, output='\n'.join([output, error])) retcode = process.poll() if retcode and ignore != 'all' and retcode not in ignore: - raise subprocess.CalledProcessError(retcode, command, output='\n'.join([output, error])) + raise subprocess.CalledProcessError(retcode, process.args, output='\n'.join([output, error])) + return output, error +def check_output(command, timeout=None, ignore=None, inputtext=None, **kwargs): + """This is a version of subprocess.check_output that adds a timeout parameter to kill + the subprocess if it does not return within the specified time.""" + process = get_subprocess(command, **kwargs) + return check_subprocess_output(process, timeout=timeout, inputtext=inputtext) + + def walk_modules(path): """ Given package name, return a list of all modules (including submodules, etc)