Skip to content

Commit 2770d5c

Browse files
rtpgJelleZijlstra
andauthored
gh-105879: Add support for keyword arguments to eval and exec (#105885)
Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 72867c9 commit 2770d5c

File tree

5 files changed

+98
-33
lines changed

5 files changed

+98
-33
lines changed

Doc/library/functions.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,11 @@ are always available. They are listed here in alphabetical order.
524524

525525
.. _func-eval:
526526

527-
.. function:: eval(expression, globals=None, locals=None)
527+
.. function:: eval(source, /, globals=None, locals=None)
528528

529-
:param expression:
529+
:param source:
530530
A Python expression.
531-
:type expression: :class:`str` | :ref:`code object <code-objects>`
531+
:type source: :class:`str` | :ref:`code object <code-objects>`
532532

533533
:param globals:
534534
The global namespace (default: ``None``).
@@ -583,11 +583,15 @@ are always available. They are listed here in alphabetical order.
583583
Raises an :ref:`auditing event <auditing>` ``exec`` with the code object
584584
as the argument. Code compilation events may also be raised.
585585

586+
.. versionchanged:: 3.13
587+
588+
The *globals* and *locals* arguments can now be passed as keywords.
589+
586590
.. index:: pair: built-in function; exec
587591

588-
.. function:: exec(object, globals=None, locals=None, /, *, closure=None)
592+
.. function:: exec(source, /, globals=None, locals=None, *, closure=None)
589593

590-
This function supports dynamic execution of Python code. *object* must be
594+
This function supports dynamic execution of Python code. *source* must be
591595
either a string or a code object. If it is a string, the string is parsed as
592596
a suite of Python statements which is then executed (unless a syntax error
593597
occurs). [#]_ If it is a code object, it is simply executed. In all cases,
@@ -640,6 +644,10 @@ are always available. They are listed here in alphabetical order.
640644
.. versionchanged:: 3.11
641645
Added the *closure* parameter.
642646

647+
.. versionchanged:: 3.13
648+
649+
The *globals* and *locals* arguments can now be passed as keywords.
650+
643651

644652
.. function:: filter(function, iterable)
645653

Lib/test/test_builtin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer
4747
HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4)
4848

49+
# used as proof of globals being used
50+
A_GLOBAL_VALUE = 123
4951

5052
class Squares:
5153

@@ -684,6 +686,11 @@ def __getitem__(self, key):
684686
raise ValueError
685687
self.assertRaises(ValueError, eval, "foo", {}, X())
686688

689+
def test_eval_kwargs(self):
690+
data = {"A_GLOBAL_VALUE": 456}
691+
self.assertEqual(eval("globals()['A_GLOBAL_VALUE']", globals=data), 456)
692+
self.assertEqual(eval("globals()['A_GLOBAL_VALUE']", locals=data), 123)
693+
687694
def test_general_eval(self):
688695
# Tests that general mappings can be used for the locals argument
689696

@@ -777,6 +784,19 @@ def test_exec(self):
777784
del l['__builtins__']
778785
self.assertEqual((g, l), ({'a': 1}, {'b': 2}))
779786

787+
def test_exec_kwargs(self):
788+
g = {}
789+
exec('global z\nz = 1', globals=g)
790+
if '__builtins__' in g:
791+
del g['__builtins__']
792+
self.assertEqual(g, {'z': 1})
793+
794+
# if we only set locals, the global assignment will not
795+
# reach this locals dictionary
796+
g = {}
797+
exec('global z\nz = 1', locals=g)
798+
self.assertEqual(g, {})
799+
780800
def test_exec_globals(self):
781801
code = compile("print('Hello World!')", "", "exec")
782802
# no builtin function
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Allow the *globals* and *locals* arguments to :func:`exec`
2+
and :func:`eval` to be passed as keywords.

Python/bltinmodule.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -925,9 +925,9 @@ builtin_divmod_impl(PyObject *module, PyObject *x, PyObject *y)
925925
eval as builtin_eval
926926
927927
source: object
928+
/
928929
globals: object = None
929930
locals: object = None
930-
/
931931
932932
Evaluate the given source in the context of globals and locals.
933933
@@ -941,7 +941,7 @@ If only globals is given, locals defaults to it.
941941
static PyObject *
942942
builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
943943
PyObject *locals)
944-
/*[clinic end generated code: output=0a0824aa70093116 input=11ee718a8640e527]*/
944+
/*[clinic end generated code: output=0a0824aa70093116 input=7c7bce5299a89062]*/
945945
{
946946
PyObject *result = NULL, *source_copy;
947947
const char *str;
@@ -1024,9 +1024,9 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
10241024
exec as builtin_exec
10251025
10261026
source: object
1027+
/
10271028
globals: object = None
10281029
locals: object = None
1029-
/
10301030
*
10311031
closure: object(c_default="NULL") = None
10321032
@@ -1044,7 +1044,7 @@ when source is a code object requiring exactly that many cellvars.
10441044
static PyObject *
10451045
builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
10461046
PyObject *locals, PyObject *closure)
1047-
/*[clinic end generated code: output=7579eb4e7646743d input=f13a7e2b503d1d9a]*/
1047+
/*[clinic end generated code: output=7579eb4e7646743d input=25e989b6d87a3a21]*/
10481048
{
10491049
PyObject *v;
10501050

Python/clinic/bltinmodule.c.h

Lines changed: 59 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)