exec
will always return None
. From the documentation:
exec(object[, globals[, locals]])
This function supports dynamic
execution of Python code. object must be either a string or a code
object. If it is a string, the string is parsed as a suite of Python
statements which is then executed (unless a syntax error occurs). [1]
If it is a code object, it is simply executed. In all cases, the code
that’s executed is expected to be valid as file input (see the section
“File input” in the Reference Manual). Be aware that the
return
and
yield
statements may not be used outside of function definitions even
within the context of code passed to the exec()
function. The return
value is None
.
This is a rather strange request. But you can capture the output like this:
>>> s = """The number {% (c) print(x) %} is a random number between 1 and 6
... inclusive. If we multiply it by 2, we get {% (d) print(2*x) %}.
...
... What's interesting is that the statements may appear out of order in the
... document. {% (a) import random %} Thus I might generate the random
... number in a location in the document well after referencing it.
... {% (b) x = random.randint(1,6) %}"""
>>> import re
>>> stmts = re.findall(r'{%s*((w*))s*(.*)%}',s)
>>> stmts
[('c', 'print(x) '), ('d', 'print(2*x) '), ('a', 'import random '), ('b', 'x = random.randint(1,6) ')]
Now, you have to redirect output to some stream which you can manipulate later:
>>> import io
>>> import sys
>>> stream = io.StringIO()
>>> stdout = sys.stdout # this keeps stdout so we can set it back
>>> sys.stdout = stream
>>> for _, statement in sorted(stmts):
... exec(statement)
...
>>> sys.stdout = stdout # remember to reset stdout!
And now, you can get the values that were printed:
>>> stream.getvalue()
'5
10
'
>>> stream.getvalue().split()
['5', '10']
Although, I think an easier way is to pass a namespace to the dict:
>>> namespace = {}
>>> for _, statement in sorted(stmts):
... exec(statement, namespace)
...
5
10
>>> namespace.keys()
dict_keys(['__builtins__', 'random', 'x'])
The namespace will get loaded with the normal __builtins__
unless you provide one yourself. So to get every name created in your executed code, you can find the difference between the namspace.keys
dictview
and a set contiaining the string "__builtins__"
>>> namespace.keys()
dict_keys(['__builtins__', 'random', 'x'])
>>> vals = namespace.keys() - {'__builtins__'}
>>> vals
{'random', 'x'}
>>> for val in vals:
... print(namespace[val])
...
<module 'random' from '/Users/juan/anaconda3/lib/python3.5/random.py'>
5
>>>
Although, if you are on python 3.4 >= it's a lot easier to redirect stdout to some stream:
>>> import contextlib
>>> stream = io.StringIO()
>>> with contextlib.redirect_stdout(stream):
... for _, statement in stmts:
... exec(statement)
...
>>> stream.getvalue()
'5
10
'
>>> stream.getvalue().split()
['5', '10']