4

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.

2
  • 1
    I'm curious why you want to do this rather than importing file.py. Anything imported automatically executes all code not under a if __name__ == '__main__' clause, and you can also use functions explicitly like file.proc or file.b. Although your question is interesting just from the mystery angle of what you've pointed out - perhaps that's why you posted rather than looking for a workaround. Commented Oct 5, 2018 at 21:13
  • Because it's not calling the same thing all the time--my program reads arbitrary scripts from a database table and executes them on an as-needed basis. Commented Oct 9, 2018 at 13:47

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Pasting the code into main actually wouldn't produce the same error; def b() would define b locally inside main, but all attempts to access b would be inside main too, so things would be fine. The difference with exec is that trying to assign variables (def counts as assignment) is undefined behavior with the default locals, and functions defined with exec don't see locals in the exec scope.
Thanks, I incorporated this.
1

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()

1 Comment

No, see comment above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.