Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: weird "NameError: name ... is not defined" in an 'exec' environment

I was confident to have at least some basic understanding of Python's scope system. Now I get an error and unfortunately I'm not even able to write a good code snippet for reproduction so far. I tried to reproduce it in a new small project, but everything works as I expect there :-/

I can only describe what I do and hopefully somebody detects a pattern and can tell me what possibly goes wrong here.

At first there is a python file x.py which implements a class X.

In some other python file, there is the following string:

code="""
...
from x import X
...
class Y(X): # does not crash here, ...
    def __init__(self):
        X.__init__(self) # ... but here
        ...
foo=Y()
"""

You can assume that python is able to find the x module. At some place, I try to execute that:

exec(code, globals(), locals())

And now I get the NameError. It tells me that X is not defined when it tries to call it's constructor. It was obviously defined a few lines above.

If I modify Y.__init__ with adding from x import X as first line, it works. But why the hell do I have to import it again there?

As already indicated, the actual code is more complex and does more things. In an unlucky case, my posting does not even show the part which actually leads to the problem. But maybe you have some general ideas, how one can get such a behaviour.

like image 729
ginger Avatar asked Apr 30 '15 23:04

ginger


1 Answers

This is just a guess, because you haven't shown us enough code, and what you've shown us doesn't actually reproduce the problem, but…

If you're doing this exec inside a function, then locals() and globals() are going to be different. In which case the code will be executed as if it were inside a class definition. So, it'll be (sort of) as if you did this:

class _:
    from x import X
    class Y(X): # does not crash here, ...
        def __init__(self):
            X.__init__(self) # ... but here
    foo=Y()
del _

(I previously thought you'd have to also be doing something like Y() outside the exec, but user2357112's answer convinced me that isn't necessary.)

If that's your problem, you may be able to fix it by just calling exec(code, globals(), globals()) or exec(code, locals(), locals()). (Which one is appropriate, if either, depends on what you're actually trying to do, of course, which you haven't told us.)

like image 152
abarnert Avatar answered Sep 19 '22 15:09

abarnert