1

Sorry but the situation a bit complicated that I can't describe it clearly in the title.

So this is the script to be imported later in exec:

# script_to_be_imported.py

def f():
    print("Hello World!")

And this is my main script:

# main.py

script = """
import script_to_be_imported
def function_to_use_the_imported_script():
    script_to_be_imported.f()

function_to_use_the_imported_script()
"""


def function_invokes_exec():
    exec(script)


function_invokes_exec()

I am using Python 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32, and it tells me that:

Traceback (most recent call last):
  File "C:\Users\yueyinqiu\Documents\MyTemporaryFiles\stackoverflow\importInExec\main.py", line 16, in <module>
    function_invokes_exec()
  File "C:\Users\yueyinqiu\Documents\MyTemporaryFiles\stackoverflow\importInExec\main.py", line 13, in function_invokes_exec
    exec(script)
  File "<string>", line 6, in <module>
  File "<string>", line 4, in function_to_use_the_imported_script
NameError: name 'script_to_be_imported' is not defined

But when I make some small changes which I think they are unrelated, it could work correctly.

For example, it works when exec is invoked outside the function:

# main.py

script = """
import script_to_be_imported
def function_to_use_the_imported_script():
    script_to_be_imported.f()

function_to_use_the_imported_script()
"""

exec(script)

and also works when:

# main.py

script = """
import script_to_be_imported
script_to_be_imported.f()
"""


def function_invokes_exec():
    exec(script)


function_invokes_exec()

It even works when a value is passed to global although it's just an empty dictionary:

# main.py

script = """
import script_to_be_imported
def function_to_use_the_imported_script():
    script_to_be_imported.f()

function_to_use_the_imported_script()
"""


def function_invokes_exec():
    exec(script, {})


function_invokes_exec()

So have I misunderstood something? Or it is a bug of python?

1
  • Additional arguments of exec(...) let you specify the local and global namespaces to.be used, separately, as dicts. (This is reldvant for Python 3.) Commented Oct 11, 2023 at 13:10

1 Answer 1

0

From documentation:

...if the optional parts are omitted, the code is executed in the "current scope".

The current scope here is the local namespace of the function_invokes_exec function.

If you look closely, the error comes from this line script_to_be_imported.f() inside function_to_use_the_imported_script function.

before function_to_use_the_imported_script gets called, what do we have in the local scope of function_invokes_exec?

  1. script_to_be_imported
  2. function_to_use_the_imported_script

Now when function_to_use_the_imported_script gets called and encounters a name, it should either look at "its" local namespace or global module(forget about nonlocal namespace for now[1]). script_to_be_imported is not in its local, is it inside the global namespace? No! So it can't find it. Where is it? It's in function_invokes_exec's local namespace. Does function_to_use_the_imported_script have access to that namespace? No.

Beside your mentioned cases, if you import script_to_be_imported in the global namespace it would also work:

# main.py
import script_to_be_imported

script = """
def function_to_use_the_imported_script():
    script_to_be_imported.f()

function_to_use_the_imported_script()
"""

def function_invokes_exec():
    exec(script)

function_invokes_exec()

[1]: Actually if a function is defined "inside" another function, it has access to the enclosing namespace. It's called "nonlocal" namespace. But here since function_to_use_the_imported_script hasn't been defined there, I didn't talk about it. Thanks to @Kellybundy for bringing the point up.

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.