planex icon indicating copy to clipboard operation
planex copied to clipboard

utils.run should have an option to echo live stdout and stderr

Open euanh opened this issue 10 years ago • 1 comments

Currently utils.run(cmd) swallows stdout and stderr and by default only prints them if cmd fails.

euanh avatar Nov 13 '15 16:11 euanh

This will be much easier with python 3.4+ (even better, 3.5). A solution only requiring smsll-ish dependencies is using trollius, a backport of asyncio present in epel. The command would look something like:

import trollius
from trollius import From, Return

@trollius.coroutine
def _read_stream(stream, cbk):
    """
    Read from stream line by line until EOF and run the callback cb
    on the lines.
    """
    while True:
        line = yield From(stream.readline())
        if line:
            cbk(line)
        else:
            break


@trollius.coroutine
def _stream_subprocess(cmd, stdout_cb, stderr_cb, env):
    """
    Capture cmd's stdout, stderr (line by line) executing the stdout_cb and
    stderr_cb callbacks as they arrive.
    """
    process = yield From(trollius.create_subprocess_exec(
        *cmd,
        stdout=trollius.subprocess.PIPE, stderr=trollius.subprocess.PIPE,
        env=env))

    yield From(trollius.wait([
        _read_stream(process.stdout, stdout_cb),
        _read_stream(process.stderr, stderr_cb)
    ]))
    yield From(process.communicate())
    raise Return(process.returncode)


def execute(cmd, stdout_cb, stderr_cb, env):
    """
    Run cmd and process stdout and stderr concurrently line by line.
    """
    loop = trollius.get_event_loop()
    retc = loop.run_until_complete(
        _stream_subprocess(
            cmd,
            stdout_cb,
            stderr_cb,
            env
        ))
    return retc


def run_with_live_stdout(cmd, env=None):
    """
    Run cmd in a subprocess, piping out the stdout.
    """

    if env is None:
        env = os.environ.copy()

    stderr = []
    print("+ %s" % " ".join(cmd))
    ret = execute(cmd, lambda o: print(o, end=""), stderr.append, env)

    if ret != 0:
        print(" ".join(stderr))
        raise Exception("Error while running\n\t%s" % " ".join(cmd))

mseri avatar Mar 28 '17 09:03 mseri