Issue36426
Created on 2019-03-25 17:31 by schperplata, last changed 2022-04-11 14:59 by admin. This issue is now closed.
| Messages (8) | |||
|---|---|---|---|
| msg338812 - (view) | Author: Domen Jurkovič (schperplata) | Date: 2019-03-25 17:31 | |
I've reported a stack overflow question and no reasonable explation was offered. Here is what I've discovered:
.. code-block:: python
def func():
varName = 'bar'
varValue = 42
localVarToEvaluate = varName + ' = varValue'
try:
exec(localVarToEvaluate)
except Exception as err:
print(str(err))
if 'bar' in locals():
# print(locals()['bar']) # (1) OK
# print(bar) # (2) ERR
#print("'bar' OK:", bar) # (3) ERR
pass # uncomment any line above
func()
After ``exec()`` is executed, ``bar`` can be seen in ``locals()``, but not accessible by intereter. Also, It can be accessed by directly calling ``print(locals()['bar'](``, but not ``print(bar)``.
This is the problem as long as the code is wrapped in function. If the same code is placed in the module body, works as expected.
Is there any exaplanation for such behaviour, or is this a bug?
|
|||
| msg338813 - (view) | Author: Domen Jurkovič (schperplata) | Date: 2019-03-25 17:33 | |
Seems like I don't know how to write a code here. Anyway, issue created on stack overflow can be found on: https://stackoverflow.com/questions/55239875/python-exec-function-broken-in-versions-above-2-7-error-name-not-defined/55240000?noredirect=1#comment97362021_55240000 Works on 2.7, fails on everything above 3.x |
|||
| msg338844 - (view) | Author: Emmanuel Arias (eamanu) * | Date: 2019-03-26 01:05 | |
I test on 3.5 and 3.8 running not in an func and I don't have the problem:
Python 3.8.0a2+ (heads/bpo-36287:ba8f342623, Mar 25 2019, 21:57:16)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 'bar'
>>> b = 42
>>> c = a + ' = c'
>>> c
'bar = c'
>>> exec(c)
>>> 'bar' in locals()
True
>>> print(locals()['bar'])
bar = c
>>> print(bar)
bar = c
>>> print("'bar' OK:", bar)
'bar' OK: bar = c
|
|||
| msg338845 - (view) | Author: Emmanuel Arias (eamanu) * | Date: 2019-03-26 01:07 | |
But I confirmed the behavior reported uhmm weird |
|||
| msg338849 - (view) | Author: Steve Dower (steve.dower) * ![]() |
Date: 2019-03-26 03:31 | |
This is currently by design, which means 3.8 is likely the only viable place it can change. It's also not Windows specific so I removed that component (people may remove themselves from nosy). But +Nick, since I know he has some interest in making locals() behave more consistently. Currently it's basically a read-only proxy, as locals are optimized within functions which is why you can't see updates via the duct. |
|||
| msg339173 - (view) | Author: Terry J. Reedy (terry.reedy) * ![]() |
Date: 2019-03-30 05:10 | |
This is not a bug; the behavior is covered in the docs. I am surprised that this works in 2.7, as it has the same warning as 3.7:
"
locals()
Update and return a dictionary representing the current local symbol table. Free variables are returned by locals() when it is called in function blocks, but not in class blocks.
Note The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter."
|
|||
| msg339183 - (view) | Author: Domen Jurkovič (schperplata) | Date: 2019-03-30 07:24 | |
Are there any workarounds - how can we use exec() and on-the-fly created variables? Also, why 'locals()['bar']' is seen by the interpretter, but not ony 'bar'? |
|||
| msg339189 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2019-03-30 11:56 | |
This is not a bug - to enable function level optimisations, the compiler must be able to see all local variable names at compile time. In Python 2.x the exec statement implementation included special code to allow even function local variables to be rebound, but that special-casing was removed as part of the Python 3 change to convert exec from a statement to a builtin function (as per https://www.python.org/dev/peps/pep-3100/#core-language ) This means that to use exec() and reliably see the changes it makes to a namespace, you have to supply a reliably read/write dictionary of your own. Object instance dictionaries work well for this purpose, as you can then access the results as attributes on the instance: ``` >>> class Namespace: ... pass ... >>> def f(): ... ns = Namespace() ... exec("x = 1; y = 2", vars(ns)) ... print(ns.x) ... print(ns.y) ... >>> f() 1 2 ``` |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:59:13 | admin | set | github: 80607 |
| 2019-03-30 11:56:11 | ncoghlan | set | status: open -> closed resolution: not a bug messages: + msg339189 stage: resolved |
| 2019-03-30 07:24:57 | schperplata | set | messages: + msg339183 |
| 2019-03-30 05:10:00 | terry.reedy | set | type: behavior -> enhancement messages:
+ msg339173 |
| 2019-03-26 03:31:31 | steve.dower | set | versions:
+ Python 3.8, Python 3.9, - Python 2.7, Python 3.5, Python 3.6, Python 3.7 nosy: + ncoghlan messages: + msg338849 components: - Windows |
| 2019-03-26 01:07:12 | eamanu | set | messages: + msg338845 |
| 2019-03-26 01:05:01 | eamanu | set | nosy:
+ eamanu messages: + msg338844 |
| 2019-03-25 17:33:54 | schperplata | set | messages: + msg338813 |
| 2019-03-25 17:31:05 | schperplata | create | |
