Prog1020
Prog1020

Reputation: 4781

Redirect print to string list?

I know how to redirect print to a file.

import sys

orig_stdout = sys.stdout
f = file('out.txt', 'w')
sys.stdout = f

for i in range(2):
    print ('i = ', i)

sys.stdout = orig_stdout
f.close()

I need to do the same but w/out a file: keep print output in a string list. How to do it in Py3k?

Edit: I can have 3rd party prints in a middle part, not my own prints, so code must be universal for usual "print()".

Upvotes: 17

Views: 13582

Answers (4)

roippi
roippi

Reputation: 25974

Instead of rolling your own class, I think it's easiest to replace sys.stdout (which is simply a TextIOWrapper) with a StringIO instance you keep a reference to:

import sys
from io import StringIO

s = StringIO()

sys.stdout = s

print('yo')

print('this is stuff')

print('hi')

s.getvalue()
Out[38]: 'yo\nthis is stuff\nhi\n'

s.getvalue().splitlines()
Out[39]: ['yo', 'this is stuff', 'hi']

As @unutbu says, you can restore the original stdout with sys.stdout = sys.__stdout__; I particlarly like the idea of using a context manager to temporarily redirect stdout to where you want it to go.

Upvotes: 10

unutbu
unutbu

Reputation: 880199

import sys
class ListStream:
    def __init__(self):
        self.data = []
    def write(self, s):
        self.data.append(s)

sys.stdout = x = ListStream()

for i in range(2):
    print ('i = ', i)

sys.stdout = sys.__stdout__
print(x.data)

yields

['i = ', ' ', '0', '\n', 'i = ', ' ', '1', '\n']

Tip: You don't need to save the original sys.stdout

orig_stdout = sys.stdout

since sys.stdout can be reset with

sys.stdout = sys.__stdout__

You could also add some syntactic sugar by making ListStream a contextmanager:

import sys
class ListStream:
    def __init__(self):
        self.data = []
    def write(self, s):
        self.data.append(s)
    def __enter__(self):
        sys.stdout = self
        return self
    def __exit__(self, ext_type, exc_value, traceback):
        sys.stdout = sys.__stdout__  

By adding the __enter__ and __exit__ methods, you can now use ListStream in a with-statement which will automatically reset sys.stdout for you when Python exits the with-suite:

with ListStream() as x:
    for i in range(2):
        print ('i = ', i)

print(x.data)

Upvotes: 19

zmo
zmo

Reputation: 24812

That's something I often do when I need to build a ncurses application:

import sys

# in this wrapper class you can use a string list instead of a full string like I'm doing
class StdOutWrapper:
    lines = []
    def write(self,txt):
        self.lines.append(txt)

    # here is a method so you can get stuff out of your wrapper class
    # I am rebuilding the text, but you can do whatever you want!
    def get_text(self,beg,end):
        return '\n'.join(self.lines)

mystdout = StdOutWrapper()
sys.stdout = mystdout
sys.stderr = mystdout

# do your stuff here that needs to be printed out in a string list
for i in range(2):
    print ('i = ', i)

# you don't need to make your variable to cache the `stdout`/`stderr` as they still exist
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

it is working fine with python 3 and python 2.

Upvotes: 2

Adam Smith
Adam Smith

Reputation: 54213

I would write a function to do it for you, rather than trying to redirect stdout to a list (which I don't think could possibly work anyway, but don't quote me on that).

def lprint(text):
    global string_list
    try: string_list.append(text)
    except NameError as e:
        string_list = [text]

for i in range(2):
    lprint ("i = {}".format(i))

print(string_list)
[OUT]: ["i = 0","i = 1"]

Upvotes: 0

Related Questions