57

I want to get the output of an exec(...) Here is my code:

code = """
i = [0,1,2]
for j in i :
    print j
"""
result = exec(code)

How could I get the things that print outputed? How can I get something like:

0
1
2

Regards and thanks.

7 Answers 7

73

Since Python 3.4 there is a solution is the stdlib: https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout

from io import StringIO
from contextlib import redirect_stdout

f = StringIO()
with redirect_stdout(f):
    help(pow)
s = f.getvalue()

In older versions you can write a context manager to handle replacing stdout:

import sys
from io import StringIO
import contextlib

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

code = """
i = [0,1,2]
for j in i :
    print j
"""
with stdoutIO() as s:
    exec(code)

print("out:", s.getvalue())
Sign up to request clarification or add additional context in comments.

5 Comments

i've got a :File "D:\Documents\perso\dev\meta\Server.py", line 77, in decompress_html with self.stdoutIO() as s: AttributeError: exit
@user462794: It seems you ignored the @contextlib.contextmanager line
You should wrap the last two lines of the generator function in try-finally, though.
contextlib has a redirect_stdout out of the box. Is there any reason you didn't use this in your solution? Seems to work well.
@julianhatwell Well, the answer is 10 years old, it didn't exist back then :-) I've added your comment, thanks.
18

You can redirect the standard output to a string for the duration of the exec call:

Python2

import sys
from cStringIO import StringIO

code = """
i = [0,1,2]
for j in i:
    print(j)
"""

old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
exec(code)
sys.stdout = old_stdout

print(redirected_output.getvalue())

Python3

import sys
from io import StringIO

code = """
i = [0,1,2]
for j in i:
    print(j)
"""

old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
exec(code)
sys.stdout = old_stdout

print(redirected_output.getvalue())

1 Comment

Just wanted to add the note that to make this Python 3 friendly, you have to import StringIO from io => from io import StringIO.
13

Here is Py3-friendly version of @Jochen's answer. I also added try-except clause to recover in case of errors in the code.

import sys
from io import StringIO
import contextlib

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

code = """
i = [0,1,2]
for j in i :
    print(j)
"""
with stdoutIO() as s:
    try:
        exec(code)
    except:
        print("Something wrong with the code")
print("out:", s.getvalue())

2 Comments

Why yield? As opposed to what sergzach is doing below you?
any ideas about why this fails for me under Python 3.4? (always same error, no matter what I put inside my print function): Something wrong with the code: print() argument after * must be a sequence, not map
4

Python 3: Get the output of the exec into a variable

import io, sys
print(sys.version)

#keep a named handle on the prior stdout 
old_stdout = sys.stdout 
#keep a named handle on io.StringIO() buffer 
new_stdout = io.StringIO() 
#Redirect python stdout into the builtin io.StringIO() buffer 
sys.stdout = new_stdout 

#variable contains python code referencing external memory
mycode = """print( local_variable + 5 )""" 

local_variable = 2
exec(mycode)

#stdout from mycode is read into a variable
result = sys.stdout.getvalue().strip()

#put stdout back to normal 
sys.stdout = old_stdout 
 
print("result of mycode is: '" + str(result) + "'") 

Prints:

3.4.8
result of mycode is: '7'

Also a reminder that python exec(...) is evil and bad because 1. It makes your code into unreadable goto-spaghetti. 2. Introduces end-user code injection opportunities, and 3. Throws the exception stacktrace into chaos because exec is made of threads and threads are bad mmkay.

Comments

3

Here is a small correction of Frédéric's answer. We need to handle a possible exception in exec() to return back normal stdout. Otherwise we could not see farther print outputs:

code = """
i = [0,1,2]
for j in i :
print j
"""

from cStringIO import StringIO
old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
try:
    exec(code)
except:
    raise 
finally: # !
    sys.stdout = old_stdout # !

print redirected_output.getvalue()
...
print 'Hello, World!' # now we see it in case of the exception above

Comments

1

Something like:

codeproc = subprocess.Popen(code, stdout=subprocess.PIPE)
print(codeproc.stdout.read())

should execute the code in a different process and pipe the output back to your main program via codeproc.stdout. But I've not personally used it so if there's something I've done wrong feel free to point it out :P

1 Comment

i've got a : codeproc = subprocess.Popen(command, stdout=subprocess.PIPE) File "C:\DEV\Python27\lib\subprocess.py", line 672, in init errread, errwrite) File "C:\DEV\Python27\lib\subprocess.py", line 882, in _execute_child startupinfo) WindowsError: [Error 2] Le fichier spécifié est introuvable (file not found in french)
-1

You could combine exec with eval to get an output as list:

ExecString('i = [0,1,2]');
println(EvalStr('[j for j in i]'));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.