Name of the first parameter which indicates that it can be an expression for eval() or a file input for exec() or a code object for either function.
Suggestion: exec(source_or_code, âŚ) and eval(expr_or_code, âŚ)
When a code object is passed, the result of executing it is returned. If it was compiled with mode=âevalâ, this is the evaluation value. If it was compiled with mode=âexecâ, the result is None.
Any assignments or deletions performed while executing the code (whether supplied as an argument or compiled from a string) are performed in the dictionary given as the locals argument.
Other than the actual differences proposed above, the documentation for both functions should be identical.
Implementation suggestion:
I would replace the builtin_exec[_impl] and builtin_eval[_impl] functions with a single builtin_exec_eval_impl function which has an additional boolean is_exec argument. The only effect of this flag is when a string is passed as the first argument.
For exec(), Py_file_input is passed to PyRun_String() or PyRun_StringFlags(), and None is returned.
For eval(), Py_eval_input is passed to PyRun_String() or PyRun_StringFlags(), and the result is returned.
I really like the idea of moving towards a unification and simplification these two functions. A couple ideas:
dictionary or subclass . This is an error in the doc.
I donât think this is an error. Subclasses are dictionaries. In OO terminology, we say a subclass object is a superclass object. The documentation should only specify if subclasses werenât allowed.
I like this, and itâs transparent since theyâre positional-only arguments.
Does that change behaviour? That might be hard to sell.
Iâve always liked simplifications to documentation. It might be easiest for the reader if exec says something like âThis function calls eval butâŚâ This makes it easy for the reader to understand exactly how exec and eval differ.
The doc for eval() states " globals must be a dictionary." This is the actual behavior.
The doc for exec() states âit must be a dictionary (and not a subclass of dictionary)â This is the error I alluded to. The actual behavior is to accept any dictionary (same as eval()).
Does that change behaviour? That might be hard to sell.
No.
As far as I can tell by reading the C code, this is the actual current behavior. The locals argument is given to PyEval_Eval[Ex] or PyRun_String[Flags]. The latter eventually calls the former with the same locals.
Hereâs an example:
>>> s = '''
... print(locals())
... x = 4
... del y
... print(locals())
... '''
>>> d = D(y = 3)
>>> d
{'y': 3}
>>> exec(s, globals(), d)
{'y': 3}
{'x': 4}
>>> d
{'x': 4}
This shows that the exec() adds x and removes y from d.
Thatâs not a matter of interpretation. Itâs clearly stated in the docs. The docstring is less clear.
âââIf only globals is provided, it must be a dictionary (and not a subclass of dictionary), which will be used for both the global and the local variables.âââ
help(exec)
âââThe globals must be a dictionary and locals can be any mappingâŚâââ
But the current CPython implementation is quite happy to use a subclass, which makes the docs page wrong. It is not, however, willing to use an arbitrary object. IMO this is a docs bug and it should be brought in line with the docstring (by removing the âand not a subclassâ parenthesis).
(Iâm not sure why globals has to be a dict but locals can be any mapping. Either way, it uses getitem/setitem correctly.)
If the code declares a variable as global, assignments and deletions operate on globals. If a variable is a closure, assignments and deletions operate on the cell object. Otherwise assignments and deletions operate on locals. If the locals argument is omitted, then globals and locals are the same dict.
Also, for both exec and eval, the first assignment is to add __builtins__ to globals if it doesnât already exist. The value of __builtins__ in globals is set as the __builtins__ attribute of any function thatâs instantiated with the given globals. When the function is called, __builtins__ is set as the f_builtins attribute of the executing frame.