52

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.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Bussiere
  • 500
  • 13
  • 60
  • 119

7 Answers7

66

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())
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
13

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())
JayRizzo
  • 3,234
  • 3
  • 33
  • 49
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 5
    Just wanted to add the note that to make this Python 3 friendly, you have to import `StringIO` from `io` => `from io import StringIO`. – idjaw Sep 13 '16 at 01:53
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())
Ilya V. Schurov
  • 7,687
  • 2
  • 40
  • 78
  • Why yield? As opposed to what sergzach is doing below you? – Hairy Feb 14 '20 at 06:11
  • 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_ – abu Mar 14 '23 at 18:36
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
sergzach
  • 6,578
  • 7
  • 46
  • 84
3

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.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
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

Blam
  • 2,888
  • 3
  • 25
  • 39
  • 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) – Bussiere Oct 11 '10 at 13:32
0

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

ExecString('i = [0,1,2]');
println(EvalStr('[j for j in i]'));
Max Kleiner
  • 1,442
  • 1
  • 13
  • 14