python: get the print output in an exec statement
Question:
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.
Answers:
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 😛
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())
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())
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())
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
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.
You could combine exec with eval to get an output as list:
ExecString('i = [0,1,2]');
println(EvalStr('[j for j in i]'));
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.
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 😛
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())
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())
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())
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
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.
You could combine exec with eval to get an output as list:
ExecString('i = [0,1,2]');
println(EvalStr('[j for j in i]'));