Consider the following:
def test(s):
globals()['a'] = s
sandbox = {'test': test}
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)
'a' in sandbox # returns False, !What I dont want!
'b' in sandbox # returns True, What I want
'a' in globals() # returns True, !What I dont want!
'b' in globals() # returns False, What I want
I'm not even sure how to ask, but I want the global scope for a function to be the environment I intend to run it in without having to compile the function during the eval. Is this possible?
Thanks for any input
Solution
def test(s):
globals()['a'] = s
sandbox = {}
# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
test.func_closure)
# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)
'a' in sandbox # returns True
'b' in sandbox # returns True
'a' in globals() # returns False
'b' in globals() # returns False
When you call a function in Python, the global variables it sees are always the globals of the module it was defined in. (If this wasn't true, the function might not work -- it might actually need some global values, and you don't necessarily know which those are.) Specifying a dictionary of globals with exec or eval() only affects the globals that the code being exec'd or eval()'d sees.
If you want a function to see other globals, then, you do indeed have to include the function definition in the string you pass to exec or eval(). When you do, the function's "module" is the string it was compiled from, with its own globals (i.e., those you supplied).
You could get around this by creating a new function with the same code object as the one you're calling but a different func_globals attribute that points to your globals dict, but this is fairly advanced hackery and probably not worth it. Still, here's how you'd do it:
# create a sandbox globals dict
sandbox = {}
# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
test.func_closure)
# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With