8

Possible Duplicate:
Can I redirect the stdout in python into some sort of string buffer?

I have a function in python that prints something to the standard output

def foo():
    print("some text")

I want to 'redirect' the text that is being printed in this function into a variable, i.e. 'wrap' this function or whatever so that the text is stored in a variable:

text = wrapper(foo)

Is there a robust way to temporarily change sys.stdout or to open a variable as a FileObject, or something else?

Community
  • 1
  • 1
Alex
  • 41,580
  • 88
  • 260
  • 469
  • Why not simply use your own print function? – phant0m Jan 07 '13 at 13:32
  • The function definition is given, I cannot the function itself. – Alex Jan 07 '13 at 13:33
  • 1
    Do we assume that you can't modify the function and just change the print to a `return`? And that you wish to temporarily modify `sys.stdout` during the function (ie decorate it) to capture it? – Jon Clements Jan 07 '13 at 13:34
  • Take a look at the duplicate- it offers the solution of redirecting `sys.stdout` to a `StringIO` object. – David Robinson Jan 07 '13 at 13:34
  • Python 2 or 3? In 3 you can just redefine `print`... – l4mpi Jan 07 '13 at 13:34
  • @David: I think the link you pointed out is what I need. Thanks. – Alex Jan 07 '13 at 13:35
  • @phant0m -- Sometimes you don't actually have access to the function like that. `dis.dis` is one which prints to stdout in the standard library. – mgilson Jan 07 '13 at 14:30
  • @mgilson Sure, but it was unclear from his problem description whether such a simplistic solution would have been enough. – phant0m Jan 07 '13 at 14:56

2 Answers2

21

For python3.4+, there's a context manager for this in the standard library.

with contextlib.redirect_stdout(file_like_object):
    ...

This part of the answer was updated, but is mostly for people who are still stuck in a python2.x world

If you're stuck on an older version of python, this context manager isn't too hard to write yourself. The key is that you can update sys.stdout to whatever file-like object you want (that's what print writes to):

>>> import sys
>>> import StringIO
>>> stdout = sys.stdout  # keep a handle on the real standard output
>>> sys.stdout = StringIO.StringIO() # Choose a file-like object to write to
>>> foo() 
>>> sys.stdout = stdout
>>> foo()
bar

To create a context manager to set stdout to whatever you want when you enter the context and then have the context manager reset stdout when you __exit__ the context.

Here's a simple example using contextlib to create the context manager:

import contextlib
import sys

@contextlib.contextmanager
def stdout_redirect(where):
    sys.stdout = where
    try:
        yield where
    finally:
        sys.stdout = sys.__stdout__

def foo():
    print 'bar'

# Examples with StringIO
import StringIO

with stdout_redirect(StringIO.StringIO()) as new_stdout:
    foo()

new_stdout.seek(0)
print "data from new_stdout:",new_stdout.read()

new_stdout1 = StringIO.StringIO()
with stdout_redirect(new_stdout1):
    foo()

new_stdout1.seek(0)
print "data from new_stdout1:",new_stdout1.read()

# Now with a file object:
with open('new_stdout') as f:
    with stdout_redirect(f):
        foo()

# Just to prove that we actually did put stdout back as we were supposed to
print "Now calling foo without context"
foo()

Note:

On python3.x, StringIO.StringIO has moved to io.StringIO. Also, on python2.x, cStringIO.StringIO might be slightly more performant.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 3
    +1 for the context manager suggestion, which would probably be the cleanest approach. – l4mpi Jan 07 '13 at 13:39
  • @l4mpi: see "Redirect stdout temporarily" in [PEP 0343](http://www.python.org/dev/peps/pep-0343/). There's some other examples there that may interest you. – martineau Mar 19 '13 at 00:10
  • 1
    From Python 3.4 `contextlib.redirect_stdout()` is available as well. – alec_djinn Nov 30 '18 at 15:38
  • https://stackoverflow.com/a/22434594/848277 Here's an answer for folks using Python 3.X since the above wasn't working for me. – pyCthon Sep 20 '22 at 21:15
7

In Python 3.x, you can just redefine print.

B = []

def print(str):
    global B
    B.append(str)

def A():
    print("example")

A()

>>> B
['example']

If, for some reason, you need the built-in print back, just do:

from builtins import print
Fredrick Brennan
  • 7,079
  • 2
  • 30
  • 61