gibraltar
gibraltar

Reputation: 1708

Redirecting the output of a python function from STDOUT to variable in Python

This is what I am trying to achieve


def fun():
    runner = InteractiveConsole()
    while(True):
        code = raw_input()
        code.rstrip('\n')
        # I want to achieve the following
        # By default the output and error of the 'code' is sent to STDOUT and STDERR
        # I want to obtain the output in two variables out and err
        out,err = runner.push(code)

All the solution that I have looked at till now, use either pipes to issue separate script execution command (which is not possible in my case). Any other way I can achieve this?

Upvotes: 3

Views: 6739

Answers (3)

Eric
Eric

Reputation: 97555

import StringIO, sys
from contextlib import contextmanager

@contextmanager
def redirected(out=sys.stdout, err=sys.stderr):
    saved = sys.stdout, sys.stderr
    sys.stdout, sys.stderr = out, err
    try:
        yield
    finally:
        sys.stdout, sys.stderr = saved


def fun():
    runner = InteractiveConsole()
    while True:
        out = StringIO.StringIO()
        err = StringIO.StringIO()
        with redirected(out=out, err=err):
            out.flush()
            err.flush()
            code = raw_input()
            code.rstrip('\n')
            # I want to achieve the following
            # By default the output and error of the 'code' is sent to STDOUT and STDERR
            # I want to obtain the output in two variables out and err
            runner.push(code)
            output = out.getvalue()
        print output

In newer versions of python, this contezt manager is built in:

with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
    ...

Upvotes: 8

jbowes
jbowes

Reputation: 4150

InteractiveConsole doesn't expose any API for setting a file like object for output or errors, you'll need to monkey patch sys.stdout and sys.stderr. As always with monkey patching, be mindful of what the side effects might be. In this case, you'd be replacing the global stdin and stdout file objects with your own implementation, which might swallow up unintended output as well (especially if you're using any threads).

It would be slightly safer to 'tee' the output with something like:

import sys
import StringIO


class TeeBuffer(object):

    def __init__(self, real):
        self.real = real
        self.buf = StringIO.StringIO()

    def write(self, val):
        self.real.write(val)
        self.buf.write(val)


def fun():
    runner = InteractiveConsole()

    out = TeeBuffer(sys.stdout)
    err = TeeBuffer(sys.stderr)

    sys.stdout = out
    sys.stderr = err

    while(True):
        code = raw_input()
        code.rstrip('\n')

        out, err = runner.push(code)
        outstr = out.buf.getvalue()
        errstr = err.buf.getvalue()

    sys.stdout = out.real
    sys.stderr = err.real

Then your user still sees the output, without you having to worry about printing it back out to the correct place on each run.

Upvotes: 1

Katriel
Katriel

Reputation: 123612

You can use a context manager to redirect stdout temporarily:

@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

Used as follows:

with opened(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"

This isn't thread-safe, of course, but neither is doing this same dance manually. In single-threaded programs (for example, in scripts) it is a popular way of doing things.


It's easy to tweak this to redirect both stdout and stderr to cStringIOs:

@contextmanager
def out_redirected():
    save_stdout = sys.stdout
    save_stderr = sys.stderr
    sys.stdout = cStringIO.String()
    sys.stderr = cStringIO.String()
    try:
        yield sys.stdout, sys.stderr
    finally:
        sys.stdout = save_stdout
        sys.stderr = save_stderr

You'd use this as

with out_redirected() as out, err:
    runner.push(code)
    out.seek(0)
    print out.read()

Upvotes: 0

Related Questions