Run this code, I get:
NameError: name ‘Optional’ is not defined
Can someone explain why?
code = """
from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
x: Optional[str]
"""
def dummy():
exec(code)
# exec here works
# exec(code)
# but exec inside dummy() does not
dummy()
EDIT:
Thanks to all of you giving feedback in comments. Sorry I did not make me clear in the first place.
I am using Python 3.11 on Debian.
The confusing part is: the line "class User(BaseModel):" always works, which shows "BaseModel" should be defined, which is imported inside "code". The key difference between "BaseModel" and "Optional" is that "Optional" occurs inside a class body. To make this clear, I update the test code as the following:
code = """
from pydantic import BaseModel
from typing import Optional, List
L = List[str] # OK
class User(BaseModel): # OK
x: Optional[str] # NameError
"""
def dummy():
exec(code)
# exec works
# exec(code)
# but dummy() does not
dummy()
Run the new test code, I still get "Optional not defined" error, but as you see, using "List" before class definition is OK.
2
Answers
Possible Reason
When you run
exec
inside a function, the code it executes doesn’t automatically have access to the imports and variables from the global scope. Instead, it only sees what’s in the local scope of that function unless you explicitly provide access.That’s why the following fails with a
NameError
:But works when you run
exec
outside the function, as it has access to the global scope.Solution
You can fix this by explicitly passing the global and local namespaces to
exec
, so the code has access to everything it needs:This way, the
exec
statement inside the function gets access to theBaseModel
andOptional
imports, resolving the issue.This is explained in the docs:
And in this case,
exec
is getting two seperate objects for locals and globals, because again, going by the docs:Which means this would be like providing
globals()
andlocals()
as the arguments (inside a function, these will return different objects).So imagine you did:
You would get:
Because class bodies aren’t enclosing scopes.
One solution is to make sure you pass the same object to both the
globals
andlocals
arguments. So something like this:Would work. Or if you want everything to be executed in the global scope, then :