-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
bpo-43605: Improve the documentation to exec() and eval() #25039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -496,44 +496,38 @@ are always available. They are listed here in alphabetical order. | |||||||||
|
|
||||||||||
| .. function:: eval(expression[, globals[, locals]]) | ||||||||||
|
|
||||||||||
| The arguments are a string and optional globals and locals. If provided, | ||||||||||
| *globals* must be a dictionary. If provided, *locals* can be any mapping | ||||||||||
| object. | ||||||||||
|
|
||||||||||
| The *expression* argument is parsed and evaluated as a Python expression | ||||||||||
| (technically speaking, a condition list) using the *globals* and *locals* | ||||||||||
| dictionaries as global and local namespace. If the *globals* dictionary is | ||||||||||
| present and does not contain a value for the key ``__builtins__``, a | ||||||||||
| reference to the dictionary of the built-in module :mod:`builtins` is | ||||||||||
| inserted under that key before *expression* is parsed. That way you can | ||||||||||
| control what builtins are available to the executed code by inserting your | ||||||||||
| own ``__builtins__`` dictionary into *globals* before passing it to | ||||||||||
| :func:`eval`. If the *locals* dictionary is omitted it defaults to the | ||||||||||
| *globals* dictionary. If both dictionaries are omitted, the expression is | ||||||||||
| executed with the *globals* and *locals* in the environment where | ||||||||||
| :func:`eval` is called. Note, *eval()* does not have access to the | ||||||||||
| :term:`nested scopes <nested scope>` (non-locals) in the enclosing | ||||||||||
| environment. | ||||||||||
|
|
||||||||||
| The return value is the result of | ||||||||||
| the evaluated expression. Syntax errors are reported as exceptions. Example: | ||||||||||
| This function supports the dynamic evaluation of Python expression. The | ||||||||||
| first argument can be a string or a code object. The optional arguments | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe link this to the documentation for code objects. |
||||||||||
| specify the globals and locals respectively. If provided, *globals* must be | ||||||||||
| a dictionary. If provided, *locals* can be any mapping object. | ||||||||||
|
||||||||||
| specify the globals and locals respectively. If provided, *globals* must be | |
| a dictionary. If provided, *locals* can be any mapping object. | |
| specify the globals and locals respectively. If provided, *globals* | |
| and *locals* must be dictionaries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What?? In the source for eval/exec in Python/bltinmodule.c, the locals are checked by PyMapping_Check() while globals checked by PyDict_Check(). I'm not sure why the exception was about "'int' object is not callable" -- it seems the name a did get resolved in the locals through the Counter object, but somehow there was an action to the effect of 1(). This might be a bug! Right now I don't have enough spoons to track it down, but I should be verifying it and maybe submitting a bug report soon. Edit: working as intended, but in a tricky way: see my reply below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works as intended:
>>> ct = Counter("abcd")
>>> eval("a", {}, ct)
1But not this:
>>> eval("print(a)", {}, ct)Expected return value of eval is None (return value of print function), but the cryptic exception is raised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this:
>>> eval("print(1)", {}, ct)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
TypeError: 'int' object is not callableThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see! The locals have the highest priority in name resolution. The Counter instance (ct in the above example) behaves as if any key is in it (with initial value 0). When the machinery underlying eval does its work, it first looks up the name "print" in the locals given to it. Unfortunately, as a result of how Counter behaves, it gets what it wishes for: the count for the key "print" in the counter ct, which is the int instance 0. Then 0 gets called as the function. The evaluation boils down to 0(1) which raises TypeError.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thank you for investigating this! I learnt a lot :).
I forgot that Counter sort of behaves like a defaultdict and returns 0 for keys it doesn't have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, me too. This is the weirdest thing I've seen in Python in a while.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| In the most common case, the *expression* argument is a string, and it is | |
| When the *expression* argument is a string, it is |
Tighten the wording
Uh oh!
There was an error while loading. Please reload this page.