Mark P
Mark P

Reputation: 43

Calling exec, getting NameError though the name is defined

I have a file named file.py containing the following script:

def b():
    print("b") 
def proc():
    print("this is main")
    b()    
proc()

And I have another file named caller.py, which contains this script:

text = open('file.py').read()
exec(text)

When I run it, I get the expected output:

this is main 

b

However, if I change caller.py to this:

def main():
    text = open('file.py').read()
    exec(text)
main()

I get the following error:

this is main
Traceback (most recent call last):
  File "./caller.py", line 7, in <module>
    main()
  File "./caller.py", line 5, in main
    exec(text)
  File "<string>", line 10, in <module>
  File "<string>", line 8, in main
NameError: global name 'b' is not defined

How is function b() getting lost? It looks to me like I'm not violating any scope rules. I need to make something similar to the second version of caller.py work.

Upvotes: 4

Views: 103

Answers (2)

Florian Weimer
Florian Weimer

Reputation: 33719

exec(text) executes text in the current scope, but modifying that scope (as def b does via the implied assignment) is undefined.

The fix is simple:

def main():
    text = open('file.py').read()
    exec(text, {})

This causes text to run in an empty global scope (augmented with the default __builtins object), the same way as in a regular Python file.

For details, see the exec documentation. It also warns that modifying the default local scope (which is implied when not specifying any arguments besides text) is unsound:

The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

Upvotes: 3

yang5
yang5

Reputation: 1235

Would it work for you if you imported and called the function instead?

myfile.py

def b():
    print("b") 

def proc():
    print("this is main")
    b()

caller.py

import myfile

myfile.proc()

Upvotes: 1

Related Questions