Skip to content

locals are not passed to inner function defined in exec() #92681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sinoleao-1 opened this issue May 11, 2022 · 4 comments
Closed

locals are not passed to inner function defined in exec() #92681

sinoleao-1 opened this issue May 11, 2022 · 4 comments
Labels
docs Documentation in the Doc dir

Comments

@sinoleao-1
Copy link

Bug report

A clear and concise description of what the bug is.
Include a minimal, reproducible example (https://stackoverflow.com/help/minimal-reproducible-example), if possible.

With following code, f1 is OK, but f2 failed with the error "name 'a' is not defined"

 def f1():
    a = "hello"
    exec("print(a)")
 
 def f2():
    a = "hello"
    exec("def inner(): print(a)\ninner()")
 
 f1()
 f2()

Your environment

  • CPython versions tested on:
    Python 3.5.2 (default, Aug 10 2017, 23:51:58)
    [GCC 4.4.3] on linux

  • Operating system and architecture:
    Linux 3.10.0-1160.25.1.el7.x86_64

@sinoleao-1 sinoleao-1 added the type-bug An unexpected behavior, bug, or error label May 11, 2022
@sinoleao-1
Copy link
Author

Did the investigation with the code below, looks like the code executed by exec() inherits locals and globals from its caller method. The inner method can inherit globals further, but cannot inherit locals.

def f1():
   a = "hello"
   exec("print('locals:', str(locals()))")
   exec("print('globals:', str(list(globals().keys())))")

def f2():
   a = "hello"
   exec("def inner():print('locals:', str(locals()))\ninner()")
   exec("def inner():print('globals:', str(list(globals().keys())))\ninner()")

if __name__ == '__main__':
   f1()
   f2()

output:
locals: {'a': 'hello'}
globals: ['__file__', '__builtins__', '__loader__', '__name__', '__package__', '__spec__', '__cached__', '__doc__', 'f1', 'f2']
locals: {}
globals: ['__file__', '__builtins__', '__loader__', '__name__', '__package__', '__spec__', '__cached__', '__doc__', 'f1', 'f2']

@sweeneyde
Copy link
Member

I think this behavior is explained by the docs in the following sentence:

If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

The behavior you notice is consistent with that:

class Somthing:
    a = "hello"
    def inner():
        print(a)
    inner()

# result: NameError: name 'a' is not defined

@sweeneyde
Copy link
Member

It looks like that language was added in commit 83efd6c for issue #57766

@terryjreedy, do you think this needs further clarification in the docs? I could see how this text at the top of the paragraph could be misleading:

In all cases, if the optional parts are omitted, the code is executed in the current scope.

@sweeneyde sweeneyde added docs Documentation in the Doc dir and removed type-bug An unexpected behavior, bug, or error labels May 14, 2022
@sweeneyde
Copy link
Member

Closing as a duplicate of #68988.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir
Projects
None yet
Development

No branches or pull requests

2 participants