python: get the print output in an exec statement
Solution 1
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())
Solution 2
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())
Solution 3
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())
Solution 4
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
Solution 5
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.
Bussiere
Updated on July 09, 2022Comments
-
Bussiere almost 2 years
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.
-
Bussiere over 13 yearsi've got a :File "D:\Documents\perso\dev\meta\Server.py", line 77, in decompress_html with self.stdoutIO() as s: AttributeError: exit
-
Bussiere over 13 yearsi'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)
-
Jochen Ritzel over 13 years@user462794: It seems you ignored the
@contextlib.contextmanager
line -
idjaw almost 8 yearsJust wanted to add the note that to make this Python 3 friendly, you have to import
StringIO
fromio
=>from io import StringIO
. -
WGH about 7 yearsYou should wrap the last two lines of the generator function in try-finally, though.
-
Hairy over 4 yearsWhy yield? As opposed to what sergzach is doing below you?
-
julianhatwell about 3 yearscontextlib has a redirect_stdout out of the box. Is there any reason you didn't use this in your solution? Seems to work well.
-
Jochen Ritzel about 3 years@julianhatwell Well, the answer is 10 years old, it didn't exist back then :-) I've added your comment, thanks.