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.

Unsupported provider

classification
Title: exec() behavior - revisited
Type: behavior Stage:
Components: Documentation Versions: Python 3.0
process
Status: closed Resolution: works for me
Dependencies: Superseder:
Assigned To: georg.brandl Nosy List: beazley, flox, georg.brandl, jhylton
Priority: normal Keywords:

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:43adminsetgithub: 49081
2013-05-07 14:31:30floxsetnosy: + flox
2009-03-31 16:10:21jhyltonsetstatus: open -> closed

nosy: + jhylton
messages: + msg84818

resolution: works for me
2009-01-04 15:23:38georg.brandlsetmessages: + msg79064
2009-01-04 15:18:52beazleysetmessages: + msg79063
2009-01-04 15:13:13georg.brandlsetassignee: georg.brandl
messages: + msg79061
components: + Documentation, - Interpreter Core
nosy: + georg.brandl
2009-01-04 14:52:36beazleycreate