1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 02:00:45 +00:00

devlib.utils.misc: Use Popen.communicate(timeout=...) in check_output

Use the timeout parameter added in Python 3.3, which removes the need for the
timer thread and avoids some weird issues in preexec_fn, as it's now documented
to sometimes not work when threads are involved.
This commit is contained in:
Douglas RAILLARD 2019-12-12 17:25:47 +00:00 committed by Marc Bonnici
parent 92e16ee873
commit 98e2e51d09

View File

@ -136,9 +136,6 @@ def get_cpu_name(implementer, part, variant):
def preexec_function(): def preexec_function():
# Ignore the SIGINT signal by setting the handler to the standard
# signal handler SIG_IGN.
signal.signal(signal.SIGINT, signal.SIG_IGN)
# Change process group in case we have to kill the subprocess and all of # Change process group in case we have to kill the subprocess and all of
# its children later. # its children later.
# TODO: this is Unix-specific; would be good to find an OS-agnostic way # TODO: this is Unix-specific; would be good to find an OS-agnostic way
@ -167,13 +164,6 @@ def check_output(command, timeout=None, ignore=None, inputtext=None,
if 'stdout' in kwargs: if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.') raise ValueError('stdout argument not allowed, it will be overridden.')
def callback(pid):
try:
check_output_logger.debug('{} timed out; sending SIGKILL'.format(pid))
os.killpg(pid, signal.SIGKILL)
except OSError:
pass # process may have already terminated.
with check_output_lock: with check_output_lock:
stderr = subprocess.STDOUT if combined_output else subprocess.PIPE stderr = subprocess.STDOUT if combined_output else subprocess.PIPE
process = subprocess.Popen(command, process = subprocess.Popen(command,
@ -183,27 +173,24 @@ def check_output(command, timeout=None, ignore=None, inputtext=None,
preexec_fn=preexec_function, preexec_fn=preexec_function,
**kwargs) **kwargs)
if timeout:
timer = threading.Timer(timeout, callback, [process.pid, ])
timer.start()
try: try:
output, error = process.communicate(inputtext) output, error = process.communicate(inputtext, timeout=timeout)
if sys.version_info[0] == 3: except subprocess.TimeoutExpired as e:
# Currently errors=replace is needed as 0x8c throws an error timeout_expired = e
output = output.decode(sys.stdout.encoding or 'utf-8', "replace") else:
if error: timeout_expired = None
error = error.decode(sys.stderr.encoding or 'utf-8', "replace")
finally: # Currently errors=replace is needed as 0x8c throws an error
if timeout: output = output.decode(sys.stdout.encoding or 'utf-8', "replace")
timer.cancel() if error:
error = error.decode(sys.stderr.encoding or 'utf-8', "replace")
if timeout_expired:
raise TimeoutError(command, output='\n'.join([output or '', error or '']))
retcode = process.poll() retcode = process.poll()
if retcode: if retcode and ignore != 'all' and retcode not in ignore:
if retcode == -9: # killed, assume due to timeout callback raise subprocess.CalledProcessError(retcode, command, output='\n'.join([output or '', error or '']))
raise TimeoutError(command, output='\n'.join([output or '', error or '']))
elif ignore != 'all' and retcode not in ignore:
raise subprocess.CalledProcessError(retcode, command, output='\n'.join([output or '', error or '']))
return output, error return output, error