exec throws an exception...why?

Discussion in 'Python' started by Nick Jacobson, Jun 6, 2004.

  1. This works fine:

    x = 1
    def execfunc():
    print x
    execfunc()

    So why doesn't this?

    s = \
    """
    x = 1
    def execfunc():
    print x
    execfunc()
    """

    codeobj = compile(s, "<string>", "exec")
    d = {}
    e = {}
    exec codeobj in d, e

    Error message:
    Traceback (most recent call last):
    File "C:\Nick\proj\python1.py", line 17, in ?
    exec codeobj in d, e
    File "<string>", line 6, in ?
    File "<string>", line 5, in execfunc
    NameError: global name 'x' is not defined

    I'm using ActiveState Python 2.3.2 on Windows XP Pro. Thanks!

    P.S. It does work if I say
    exec codeobj in d
    but I don't understand why.
     
    Nick Jacobson, Jun 6, 2004
    #1
    1. Advertising

  2. Nick Jacobson

    fishboy Guest

    On 5 Jun 2004 18:46:11 -0700, (Nick Jacobson)
    wrote:

    >This works fine:
    >
    >x = 1
    >def execfunc():
    > print x
    >execfunc()
    >
    >So why doesn't this?
    >
    >s = \
    >"""
    >x = 1
    >def execfunc():
    > print x
    >execfunc()
    >"""
    >
    >codeobj = compile(s, "<string>", "exec")
    >d = {}
    >e = {}
    >exec codeobj in d, e
    >
    >Error message:
    >Traceback (most recent call last):
    > File "C:\Nick\proj\python1.py", line 17, in ?
    > exec codeobj in d, e
    > File "<string>", line 6, in ?
    > File "<string>", line 5, in execfunc
    >NameError: global name 'x' is not defined
    >
    >I'm using ActiveState Python 2.3.2 on Windows XP Pro. Thanks!
    >
    >P.S. It does work if I say
    >exec codeobj in d
    >but I don't understand why.


    It works because the local and global namespaces are both 'd'. it
    doesn't work in the first because it puts 'x' in the local and then
    looks in global.

    Now, why it puts 'x' in local, I don't know. If this was a quiz, I'd
    put "Nested Scope" and pray.

    ><{{{*>
     
    fishboy, Jun 6, 2004
    #2
    1. Advertising

  3. fishboy <> wrote in message news:<>...
    > On 5 Jun 2004 18:46:11 -0700, (Nick Jacobson)
    > wrote:
    >
    > >This works fine:
    > >
    > >x = 1
    > >def execfunc():
    > > print x
    > >execfunc()
    > >
    > >So why doesn't this?
    > >
    > >s = \
    > >"""
    > >x = 1
    > >def execfunc():
    > > print x
    > >execfunc()
    > >"""
    > >
    > >codeobj = compile(s, "<string>", "exec")
    > >d = {}
    > >e = {}
    > >exec codeobj in d, e
    > >
    > >Error message:
    > >Traceback (most recent call last):
    > > File "C:\Nick\proj\python1.py", line 17, in ?
    > > exec codeobj in d, e
    > > File "<string>", line 6, in ?
    > > File "<string>", line 5, in execfunc
    > >NameError: global name 'x' is not defined
    > >
    > >I'm using ActiveState Python 2.3.2 on Windows XP Pro. Thanks!
    > >
    > >P.S. It does work if I say
    > >exec codeobj in d
    > >but I don't understand why.

    >
    > It works because the local and global namespaces are both 'd'. it
    > doesn't work in the first because it puts 'x' in the local and then
    > looks in global.
    >
    > Now, why it puts 'x' in local, I don't know. If this was a quiz, I'd
    > put "Nested Scope" and pray.
    >
    > ><{{{*>


    I don't know either, that's why I asked ;)

    And I don't see why assigning both the local and global namespaces to
    the variable 'd' fixes it. But to answer that, the latter question
    has to be addressed first.

    --Nick
     
    Nick Jacobson, Jun 6, 2004
    #3
  4. Nick Jacobson

    Hung Jung Lu Guest

    (Nick Jacobson) wrote:
    > > Now, why it puts 'x' in local, I don't know.

    >
    > I don't know either, that's why I asked ;)


    The name binding in assignment seems to proceed only for locals,
    unless a variable is declared as global explicitly. That is, by
    default, the supplied global dictionary is read-only, unless the
    'global' statment is used explicitly for those variables that you want
    to override. Think of this behavior as if the code defined in the s
    string were executed inside another nameless function.

    #--------------- try this first in console
    s='''
    x = 1
    def f():
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    print x
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    f()
    '''
    d={}
    e={}
    a = compile(s, '<string>', 'exec')
    exec a in d, e
    #--------------- output
    globals ['__builtins__']
    locals ['x', 'f']
    globals ['__builtins__']
    locals []
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<string>", line 9, in ?
    File "<string>", line 6, in f
    NameError: global name 'x' is not defined

    #--------------- now try this in console
    s='''
    global x
    x = 1
    def f():
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    print x
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    f()
    '''
    d={}
    e={}
    a = compile(s, '<string>', 'exec')
    exec a in d, e
    #--------------- output
    globals ['__builtins__', 'x']
    locals ['f']
    globals ['__builtins__', 'x']
    locals []

    #--------------- also try this from fresh console
    s='''
    x = 1
    def f():
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    print x
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    f()
    '''
    a = compile(s, '<string>', 'exec')
    exec a
    #--------------- output
    globals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
    locals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
    globals ['a', 's', 'x', '__builtins__', '__name__', 'f', '__doc__']
    locals []
    1

    -------------------------------------------
    Therefore, when no dictionary is supplied, it works because it uses
    the current globals() and locals() of the current scope, which in the
    case of console or module level (i.e., zero indentation) are referring
    to the same dictionary. The assignment 'x=1' was performed on the
    locals() dictionary for writing. But inside the f() function, the
    statement 'print x' pulls the value from the globals() dictionary.
    This coincidence of two dictionaries only happens when you run the
    code from the module level. If you put the above code inside a
    function, it won't work.

    #--------------- from a fresh console
    def test():
    s='''
    x = 1
    def f():
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    print x
    print 'globals', globals().keys()
    print 'locals', locals().keys()
    f()
    '''
    a = compile(s, '<string>', 'exec')
    exec a

    test()
    #--------------- output
    globals ['__builtins__', '__name__', 'test', '__doc__']
    locals ['a', 'x', 's', 'f']
    globals ['__builtins__', '__name__', 'test', '__doc__']
    locals []
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 14, in test
    File "<string>", line 9, in ?
    File "<string>", line 6, in f
    NameError: global name 'x' is not defined

    regards,

    Hung Jung
     
    Hung Jung Lu, Jun 6, 2004
    #4
  5. Thank you very much for the detailed response. But there's still a
    problem.

    > The name binding in assignment seems to proceed only for locals,
    > unless a variable is declared as global explicitly. That is, by
    > default, the supplied global dictionary is read-only, unless the
    > 'global' statment is used explicitly for those variables that you want
    > to override.


    Agreed. The global dictionary is not modified.

    But here's the thing. From the Python Ref. Manual:

    "If the [local variable] definition occurs in a function block, the
    scope extends to any blocks contained within the defining one..."

    So as long as code is not on the module level, scopes are extended.
    e.g. this works fine:

    def main():
    y = 3
    def execfunc():
    print y
    execfunc()
    if __name__ == '__main__':
    main()

    But, the following code fails, saying that y is undefined:

    def main():
    s = \
    """
    y = 3
    def execfunc():
    print y
    execfunc()
    """
    d = {}
    e = {}
    exec s in d, e

    if __name__ == '__main__':
    main()

    Why not? Because it doesn't realize it's in main()! It thinks it's
    on the module level! (I guess the indentation is a clue.)

    Now, if it WERE simply code on the module level, it would also work:

    y = 3
    def execfunc():
    print y
    execfunc()

    because as you explained, y goes in globals(). This is probably why
    the scopes don't nest at this level: they normally don't need to.

    Conclusion:

    Is code from the exec statement on the module level or not? It
    doesn't get its locals mapped to globals. But it also doesn't get its
    local variables nested. So it gets neither benefit. IMO it should
    get one or the other. i.e. the second piece of code should work.

    --Nick
     
    Nick Jacobson, Jun 7, 2004
    #5
  6. Nick Jacobson

    Hung Jung Lu Guest

    (Nick Jacobson) wrote:
    >
    > "If the [local variable] definition occurs in a function block, the
    > scope extends to any blocks contained within the defining one..."


    Strictly speaking, the above statement still holds true. That is, from
    a lawyer's point of view. :)

    > But, the following code fails, saying that y is undefined:
    >
    > def main():
    > s = \
    > """
    > y = 3
    > def execfunc():
    > print y
    > execfunc()
    > """
    > d = {}
    > e = {}
    > exec s in d, e
    >
    > if __name__ == '__main__':
    > main()


    Very good example.

    > Conclusion:
    >
    > Is code from the exec statement on the module level or not? It
    > doesn't get its locals mapped to globals. But it also doesn't get its
    > local variables nested. So it gets neither benefit. IMO it should
    > get one or the other. i.e. the second piece of code should work.


    The exec statement is usually considered an slightly advanced topic.
    And frankly I think people that use it at the module level would be
    minority. So, having its behavior parallel to the case of nested-scope
    would seem to make more sense, to me. Remember also that the "global"
    statement can be used inside the code string, which is reminiscence
    that we are inside a local scope, just like the case of function. So,
    if you wish, the inconsistency could be considered as a bug in Python,
    and it may even be good idea to submit a bug report. After all, nested
    scope is considered a relative new feature, given Python's history
    (over a decade.)

    Or there may be some obscure way of tweaking things so to convince
    Python that it is inside a function scope at the moment of "def
    execfunc():"... Anyway, we are talking about voodoo magic here... :)

    regards,

    Hung Jung
     
    Hung Jung Lu, Jun 7, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chris Miller
    Replies:
    4
    Views:
    13,568
    Chris Smith
    Nov 22, 2003
  2. Mr. SweatyFinger

    why why why why why

    Mr. SweatyFinger, Nov 28, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    979
    Mark Rae
    Dec 21, 2006
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,223
    Smokey Grindel
    Dec 2, 2006
  4. John Dalberg
    Replies:
    4
    Views:
    3,424
    Alexey Smirnov
    May 10, 2007
  5. Replies:
    2
    Views:
    357
Loading...

Share This Page