classification
Title: eval() in a list comprehension
Type: behavior Stage: resolved
Components: Documentation Versions: Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Grzegorz Krasoń, docs@python, rhettinger, steven.daprano
Priority: normal Keywords: patch

Created on 2019-07-21 23:24 by Grzegorz Krasoń, last changed 2019-08-07 01:08 by rhettinger. This issue is now closed.

Files
File name Uploaded Description Edit
demo.py Grzegorz Krasoń, 2019-07-21 23:24
Pull Requests
URL Status Linked Edit
PR 15117 merged rhettinger, 2019-08-04 20:21
PR 15155 merged miss-islington, 2019-08-07 00:57
Messages (9)
msg348271 - (view) Author: Grzegorz Krasoń (Grzegorz Krasoń) Date: 2019-07-21 23:24
eval() works in a global scope when invoked in a list comprehension.
msg348272 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2019-07-22 00:15
What leads you to believe that eval *shouldn't* work in the global scope in a comprehension?

If not the global scope, which scope should it be, local or nonlocal? Is the behaviour documented differently?

For reference, the current docs for eval are here:
https://docs.python.org/3.5/library/functions.html#eval
msg348274 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-07-22 00:25
This used to work as you expected in Python 2.

In Python 3, list comprehensions create their own inner scope just like generator expressions.  

Per the eval() docs, "if both dictionaries are omitted, the expression is executed in the environment where eval() is called." 

In your code example, the inner scope doesn't have a local variable "x", so the global variable "x" is retrieved.  

That said, I would have expected the inner "x" to be found as a non-local.  So yes, this does seem odd an it isn't really true that "the expression is executed in the environment where eval() is called."  Instead, it uses the globals() and locals() of the environment where it is called but not the nested scope.  Perhaps this should be clarified in the docs if it is in fact the intended behavior.
msg348293 - (view) Author: Grzegorz Krasoń (Grzegorz Krasoń) Date: 2019-07-22 10:22
Steven, I believed that any `<expression>` replaced by `eval('<expression>')` will not change behaviour of the code. Now I understand that my assumption was incorrect.

Raymond, thanks for helping me understand this.
msg348313 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2019-07-23 01:25
I'm not sure we should be so quick to close this. At the very least, I 
think the documentation could be improved.

It does seem desirable to have the invariant:

    `expression` == `eval("expression")`

apply in any environment. Was the change in behaviour between 2 and 3 
intentional, or just a side-effect of the change in implementation?
msg348405 - (view) Author: Grzegorz Krasoń (Grzegorz Krasoń) Date: 2019-07-24 20:52
I re-opened the issue.

Dear core developers, can we ask you to confirm if described behavior of eval() is expected?
msg349001 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-04 20:27
After more thought, I think the existing behavior is probably what we want.  There may not be a clean way to allow access and updates to non-locals.  Even if a way was found, it may tie our hands and preclude other implementation changes down the road.  Also, such a feature may be at odds with the current API which allows the execution environment to be retargeted.  There is also a risk of introducing new security issues.

I've attached a PR to update the eval() docs to reflect the actual behavior.
msg349144 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-07 00:56
New changeset 610a4823cc0a3c2380ad0dfe64ae483ced4e5304 by Raymond Hettinger in branch 'master':
bpo-37646:  Document that eval() cannot access nested scopes (GH-15117)
https://github.com/python/cpython/commit/610a4823cc0a3c2380ad0dfe64ae483ced4e5304
msg349145 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-07 01:08
New changeset 9341dcb4b9520ab92df10d4256e93a50e1e7d19f by Raymond Hettinger (Miss Islington (bot)) in branch '3.8':
bpo-37646:  Document that eval() cannot access nested scopes (GH-15117) (GH-15155)
https://github.com/python/cpython/commit/9341dcb4b9520ab92df10d4256e93a50e1e7d19f
History
Date User Action Args
2019-08-07 01:08:28rhettingersetstatus: open -> closed

nosy: + docs@python
assignee: docs@python
components: + Documentation, - Interpreter Core
resolution: fixed
stage: patch review -> resolved
2019-08-07 01:08:02rhettingersetmessages: + msg349145
2019-08-07 00:57:14miss-islingtonsetpull_requests: + pull_request14888
2019-08-07 00:56:25rhettingersetmessages: + msg349144
2019-08-04 20:27:54rhettingersetmessages: + msg349001
2019-08-04 20:21:23rhettingersetkeywords: + patch
stage: resolved -> patch review
pull_requests: + pull_request14858
2019-07-24 20:52:25Grzegorz Krasońsetmessages: + msg348405
2019-07-24 20:48:17Grzegorz Krasońsetstatus: closed -> open
resolution: not a bug -> (no value)
2019-07-23 01:25:24steven.dapranosetmessages: + msg348313
2019-07-22 10:22:13Grzegorz Krasońsetstatus: open -> closed
resolution: not a bug
messages: + msg348293

stage: resolved
2019-07-22 00:25:59rhettingersetnosy: + rhettinger
messages: + msg348274
2019-07-22 00:15:41steven.dapranosetnosy: + steven.daprano
messages: + msg348272
2019-07-21 23:24:25Grzegorz Krasońcreate