This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Exec not recognizing global variables inside function
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Huyston, Krishna Oza, ronaldoussoren, terry.reedy
Priority: normal Keywords:

Created on 2019-09-30 20:39 by Huyston, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (5)
msg353622 - (view) Author: (Huyston) Date: 2019-09-30 20:39
This seems like a bug for me, forgive me if its not.

Consider the following script:

def func():
    print(var)

my_globals = {'func':func,'var':14}
exec("print(var);func()",my_globals,my_globals)

This is the output:

14
Traceback (most recent call last):
  File "bug.py", line 5, in <module>
    exec("print(var);func()",my_globals,my_globals)
  File "<string>", line 1, in <module>
  File "bug.py", line 2, in func
    print(var)
NameError: name 'var' is not defined

The first line gives the expected result (14). However the second line throws the NameError.

If 'func' is defined inside the exec argument string, then it prints 14 and 14, which is the expected result (for me at least).

Ex:

def func():
    print(var)

my_globals = {'func':func,'var':14}
exec("def func():\n    print(var)\nprint(var);func()",my_globals,my_globals)

Result:
14
14

So is this really a bug or is this the expected behavior somehow?
msg353673 - (view) Author: Krishna Oza (Krishna Oza) * Date: 2019-10-01 11:27
adding self to nosy list.
msg353711 - (view) Author: Ronald Oussoren (ronaldoussoren) * (Python committer) Date: 2019-10-01 20:28
I don't think this is a bug.

def func():
   print(var)

This captures the globals at the time of definition, that is "global" references are resolved using the func.__globals__ attribute which is the globals dictionary at the time the function is created

exec("func()", globals=my_globals;locals=my_globals)

This runs code with a different globals dictionary, but that doesn't affect the already defined function because it resolves names with the previously captured globals dictionary.

This is basically the mechanism that ensures that functions defined in a module use that modules global namespace, and not the globals from where they are called.
msg353737 - (view) Author: (Huyston) Date: 2019-10-02 14:38
Ronald Oussoren, I understand your comment and it makes sense in the module perspective.

However, when using 'exec', we are explicitly passing a globals dict (and locals) that the target code should consider.

As the documentation states:
"If globals and locals are given, they are used for the global and local variables, respectively."

In my point of view, the function is not considering the given globals and locals dict.
msg353995 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2019-10-05 06:23
Ronald is correct, and for the reason given.  Python functions are lexically scoped, not dynamically scoped, and you are expecting the latter.  The exec global and local namespaces are used to resolve identifier in the code you pass.  Your first example passes 'func' and that is resolved to the *pre-compiled* function object which uses is lexicographical globals.  It does not contain the func-local 'var'.  You second example contains a def statement to be compiled in the exec namespaces.  Please post to python-list if you want further discussion.
History
Date User Action Args
2022-04-11 14:59:21adminsetgithub: 82512
2019-10-05 06:23:39terry.reedysetstatus: open -> closed

nosy: + terry.reedy
messages: + msg353995

resolution: not a bug
stage: resolved
2019-10-02 14:38:28Huystonsetmessages: + msg353737
2019-10-01 20:28:42ronaldoussorensetnosy: + ronaldoussoren
messages: + msg353711
2019-10-01 11:27:09Krishna Ozasetnosy: + Krishna Oza
messages: + msg353673
2019-09-30 20:39:38Huystoncreate