Skip to content
This repository has been archived by the owner on Oct 21, 2022. It is now read-only.

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

Open
euanh opened this issue Nov 13, 2015 · 1 comment
Open

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

euanh opened this issue Nov 13, 2015 · 1 comment
Labels

Comments

@euanh
Copy link
Contributor

euanh commented Nov 13, 2015

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

@euanh euanh added the bug label Nov 13, 2015
@euanh euanh added this to the next-maintenance milestone Nov 13, 2015
@mseri
Copy link
Contributor

mseri commented Mar 28, 2017

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))

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants