From 670d227cf81678eb3fa3e11f7ba3e6a167332c66 Mon Sep 17 00:00:00 2001 From: Andre Anjos <andre.dos.anjos@gmail.com> Date: Mon, 21 Jan 2019 18:18:23 +0100 Subject: [PATCH] [bootstrap] New implementation of run_cmdline to address #2 --- bob/devtools/bootstrap.py | 52 +++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/bob/devtools/bootstrap.py b/bob/devtools/bootstrap.py index 585f0132..01c415c1 100644 --- a/bob/devtools/bootstrap.py +++ b/bob/devtools/bootstrap.py @@ -36,9 +36,12 @@ _INTERVALS = ( import os import sys +import pty import glob import time +import errno import shutil +import select import platform import subprocess @@ -93,6 +96,9 @@ def human_time(seconds, granularity=2): def run_cmdline(cmd, env=None): '''Runs a command on a environment, logs output and reports status + Copied from: https://github.com/terminal-labs/cli-passthrough, which is in + turn based on https://stackoverflow.com/a/31953436. + Parameters: @@ -108,16 +114,43 @@ def run_cmdline(cmd, env=None): logger.info('(system) %s' % ' '.join(cmd)) start = time.time() - out = b'' - - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - env=env, bufsize=1, universal_newlines=True) - for line in iter(p.stdout.readline, ''): - sys.stdout.write(line) - sys.stdout.flush() - - if p.wait() != 0: + masters, slaves = zip(pty.openpty(), pty.openpty()) + + with subprocess.Popen(cmd, stdin=slaves[0], stdout=slaves[0], + stderr=slaves[1], env=env) as p: + + for fd in slaves: + os.close(fd) # no input + readable = { + masters[0]: sys.stdout.buffer, # store buffers seperately + masters[1]: sys.stderr.buffer, + } + + while readable: + + for fd in select.select(readable, [], [])[0]: + try: + data = os.read(fd, 1024) # read available + except OSError as e: + if e.errno != errno.EIO: + raise #XXX cleanup + del readable[fd] # EIO means EOF on some systems + else: + if not data: # EOF + del readable[fd] + else: + if fd == masters[0]: # We caught stdout + utils.echo(data.rstrip()) + else: # We caught stderr + utils.echo(data.rstrip(), err=True) + + readable[fd].flush() + + for fd in masters: + os.close(fd) + + if p.returncode != 0: raise RuntimeError("command `%s' exited with error state (%d)" % \ (' '.join(cmd), p.returncode)) @@ -126,7 +159,6 @@ def run_cmdline(cmd, env=None): logger.info('command took %s' % human_time(total)) - def touch(path): '''Python-implementation of the "touch" command-line application''' -- GitLab