Consider the following snippet:
def bar():
return 1
print([bar() for _ in range(5)])
It gives an expected output [1, 1, 1, 1, 1]<
Here's a solution!
We needed to get the local namespace out after the exec() to track modifications. This doesn't work with only one namespace, so we did this:
class MagickNameSpace(UserDict, dict):
"""A magic namespace for Python 3 exec().
We need separate global and local namespaces in exec(). This does not
work well in Python 3, because in Python 3 the enclosing namespaces are
not used to look up variables, which seems to be an optimization thing
as the exec'd code isn't available at module compilation.
So we make a MagickNameSpace that stores all new variables in a
separate dict, conforming to the local/enclosing namespace, but
looks up variables in both.
"""
def __init__(self, ns, *args, **kw):
UserDict.__init__(self, *args, **kw)
self.globals = ns
def __getitem__(self, key):
try:
return self.data[key]
except KeyError:
return self.globals[key]
def __contains__(self, key):
return key in self.data or key in self.globals
Replace the old code:
exec(code, global_ns, local_ns)
return local_ns
with:
ns = MagickNameSpace(global_ns)
ns.update(local_ns)
exec(code, ns)
return ns.data