Issue 4831: exec() behavior - revisited (original) (raw)

Created on 2009-01-04 14:52 by beazley, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (5)
msg79059 - (view) Author: David M. Beazley (beazley) Date: 2009-01-04 14:52
Please forgive me, but I'm really trying to wrap my brain around the behavior of exec() in Python 3. Here's a quote from the documentation: "In all cases, if the optional parts are omitted, the code is executed in the current scope." This is referring to the optional use of the globals/locals parameters and seems to indicate that if they're omitted the code executes in the scope where the exec() appeared. Yet, this code fails: def foo(): exec("a = 42") print(a) # NameError: a Now, I realize that exec() became a function in Python 3. However, regardless of that, is it really the intent that exec() not be allowed to ever modify any local variable of a function? In other words, do I really have to do this? def foo(): ldict = locals() exec("a=42",globals(),ldict) a = ldict['a'] print(a) I submitted a bug report about this once before and it was immediately dismissed. I would appreciate some greater clarity on this matter this go around. Specifically, what is the approved way to have exec() modify the local environment of a function?
msg79061 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-01-04 15:13
> Please forgive me, but I'm really trying to wrap my brain around the > behavior of exec() in Python 3. Here's a quote from the documentation: > "In all cases, if the optional parts are omitted, the code is > executed in the current scope." > This is referring to the optional use of the globals/locals parameters > and seems to indicate that if they're omitted the code executes in the > scope where the exec() appeared. That's right, but in some cases the locals can't be changed. I'll document this better. > Now, I realize that exec() became a function in Python 3. However, > regardless of that, is it really the intent that exec() not be allowed > to ever modify any local variable of a function? In other words, do I > really have to do this? > def foo(): > ldict = locals() > exec("a=42",globals(),ldict) > a = ldict['a'] > print(a) Yes, if you really need "a" as a local afterwards. > I would appreciate some greater clarity on this matter this go around. > Specifically, what is the approved way to have exec() modify the local > environment of a function? There is none. To modify the locals of a function on the fly is not possible without several consequences: normally, function locals are not stored in a dictionary, but an array, whose indices are determined at compile time from the known locales. This collides at least with new locals added by exec. The old exec statement circumvented this, because the compiler knew that if an exec without globals/locals args occurred in a function, that namespace would be "unoptimized", i.e. not using the locals array. Since exec() is now a normal function, the compiler does not know what "exec" may be bound to, and therefore can not treat is specially.
msg79063 - (view) Author: David M. Beazley (beazley) Date: 2009-01-04 15:18
One further followup just to make sure I'm clear. Is it always safe to pass the result of locals() into exec and extract the result as shown in my example? Since I'm writing about this in a book, I just want to make absolutely certain I know what's going on and that I don't tell people something that's completely bogus. Thanks!
msg79064 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-01-04 15:23
Yes, the dictionary will always contain "a" afterwards. However, a cleaner way would be to use locals().copy(), or, if you don't need current locals in the exec'd string, just a fresh dict. In scopes not using the optimized locals, there may be some other effects when using the dict returned by locals() directly: * in module scopes, the dictionary returned by locals() is the same as the globals() one * in class scopes, the locals() dict is actually the local scope of the class
msg84818 - (view) Author: Jeremy Hylton (jhylton) (Python triager) Date: 2009-03-31 16:10
I think this bug ran out of steam. Python is behaving as intended, and I think Georg addressed all of David's questions.
History
Date User Action Args
2022-04-11 14:56:43 admin set github: 49081
2013-05-07 14:31:30 flox set nosy: + flox
2009-03-31 16:10:21 jhylton set status: open -> closednosy: + jhyltonmessages: + resolution: works for me
2009-01-04 15:23:38 georg.brandl set messages: +
2009-01-04 15🔞52 beazley set messages: +
2009-01-04 15:13:13 georg.brandl set assignee: georg.brandlmessages: + components: + Documentation, - Interpreter Corenosy: + georg.brandl
2009-01-04 14:52:36 beazley create