When I try this code in a console (in PyCharm):
exec("import random")
exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)")
locals()['f']()
it works fine. But when I try to do exactly the same in my program it doesn't work, and I get the exception
NameError: name 'random' is not defined.
I found that this code doesn't raise errors:
exec("import random", globals(), globals())
exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)", globals(), globals())
globals()['f']()
But I can't understand why.
What's going on?
You're not doing "exactly the same" in your program. That exact code, copied verbatim into a file and run as a Python script, works just fine (albeit with no visible result).
What I think you might actually be doing is something like this:
def import_stuff():
exec("import random")
def do_stuff():
import_stuff()
exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)")
locals()['f']()
do_stuff()
The above code does result in the NameError
exception noted in your question, because (to quote the docs),
In all cases, if the optional parts are omitted, the code is executed in the current scope.
Since the code above imports random
into the local scope of import_stuff()
, it's not visible to do_stuff()
.
In fact, the code above is identical in behaviour to the following:
def import_stuff():
import random
def do_stuff():
import_stuff()
def f():
return random.randint(0, 10), random.randint(0, 10)
f()
do_stuff()
… which also fails, for the same reason.
Assuming this is what's actually going on in your real code, a version modified as in your question by adding globals(), globals()
arguments to exec()
would work, because then you're explicitly importing random
into the global scope, where everything can see it.