Skip to content

Commit 46496f9

Browse files
authored
bpo-42990: Functions inherit current builtins (GH-24564)
The types.FunctionType constructor now inherits the current builtins if the globals dictionary has no "__builtins__" key, rather than using {"None": None} as builtins: same behavior as eval() and exec() functions. Defining a function with "def function(...): ..." in Python is not affected, globals cannot be overriden with this syntax: it also inherits the current builtins. PyFrame_New(), PyEval_EvalCode(), PyEval_EvalCodeEx(), PyFunction_New() and PyFunction_NewWithQualName() now inherits the current builtins namespace if the globals dictionary has no "__builtins__" key. * Add _PyEval_GetBuiltins() function. * _PyEval_BuiltinsFromGlobals() now uses _PyEval_GetBuiltins() if builtins cannot be found in globals. * Add tstate parameter to _PyEval_BuiltinsFromGlobals().
1 parent 4233ff3 commit 46496f9

File tree

7 files changed

+74
-31
lines changed

7 files changed

+74
-31
lines changed

Doc/whatsnew/3.10.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,8 @@ Other Language Changes
282282
283283
* Functions have a new ``__builtins__`` attribute which is used to look for
284284
builtin symbols when a function is executed, instead of looking into
285-
``__globals__['__builtins__']``.
285+
``__globals__['__builtins__']``. The attribute is initialized from
286+
``__globals__["__builtins__"]`` if it exists, else from the current builtins.
286287
(Contributed by Mark Shannon in :issue:`42990`.)
287288
288289
@@ -789,6 +790,14 @@ Changes in the Python API
789790
(Contributed by Yurii Karabas, Andrew Svetlov, Yury Selivanov and Kyle Stanley
790791
in :issue:`42392`.)
791792
793+
* The :data:`types.FunctionType` constructor now inherits the current builtins
794+
if the *globals* dictionary has no ``"__builtins__"`` key, rather than using
795+
``{"None": None}`` as builtins: same behavior as :func:`eval` and
796+
:func:`exec` functions. Defining a function with ``def function(...): ...``
797+
in Python is not affected, globals cannot be overriden with this syntax: it
798+
also inherits the current builtins.
799+
(Contributed by Victor Stinner in :issue:`42990`.)
800+
792801
CPython bytecode changes
793802
========================
794803

Include/internal/pycore_ceval.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth(
3434
void _PyEval_Fini(void);
3535

3636

37-
extern PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals);
37+
extern PyObject* _PyEval_GetBuiltins(PyThreadState *tstate);
38+
extern PyObject *_PyEval_BuiltinsFromGlobals(
39+
PyThreadState *tstate,
40+
PyObject *globals);
3841

3942

4043
static inline PyObject*

Lib/test/test_funcattrs.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import textwrap
12
import types
23
import unittest
34

@@ -78,6 +79,32 @@ def test___builtins__(self):
7879
self.cannot_set_attr(self.b, '__builtins__', 2,
7980
(AttributeError, TypeError))
8081

82+
# bpo-42990: If globals is specified and has no "__builtins__" key,
83+
# a function inherits the current builtins namespace.
84+
def func(s): return len(s)
85+
ns = {}
86+
func2 = type(func)(func.__code__, ns)
87+
self.assertIs(func2.__globals__, ns)
88+
self.assertIs(func2.__builtins__, __builtins__)
89+
90+
# Make sure that the function actually works.
91+
self.assertEqual(func2("abc"), 3)
92+
self.assertEqual(ns, {})
93+
94+
# Define functions using exec() with different builtins,
95+
# and test inheritance when globals has no "__builtins__" key
96+
code = textwrap.dedent("""
97+
def func3(s): pass
98+
func4 = type(func3)(func3.__code__, {})
99+
""")
100+
safe_builtins = {'None': None}
101+
ns = {'type': type, '__builtins__': safe_builtins}
102+
exec(code, ns)
103+
self.assertIs(ns['func3'].__builtins__, safe_builtins)
104+
self.assertIs(ns['func4'].__builtins__, safe_builtins)
105+
self.assertIs(ns['func3'].__globals__['__builtins__'], safe_builtins)
106+
self.assertNotIn('__builtins__', ns['func4'].__globals__)
107+
81108
def test___closure__(self):
82109
a = 12
83110
def f(): print(a)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
The :data:`types.FunctionType` constructor now inherits the current builtins if
2+
the *globals* dictionary has no ``"__builtins__"`` key, rather than using
3+
``{"None": None}`` as builtins: same behavior as :func:`eval` and :func:`exec`
4+
functions. Defining a function with ``def function(...): ...`` in Python is
5+
not affected, globals cannot be overriden with this syntax: it also inherits
6+
the current builtins.
7+
Patch by Victor Stinner.

Objects/frameobject.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ PyFrameObject*
847847
PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
848848
PyObject *globals, PyObject *locals)
849849
{
850-
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
850+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);
851851
if (builtins == NULL) {
852852
return NULL;
853853
}
@@ -862,7 +862,6 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
862862
.fc_closure = NULL
863863
};
864864
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, &desc, locals);
865-
Py_DECREF(builtins);
866865
if (f) {
867866
_PyObject_GC_TRACK(f);
868867
}
@@ -1163,29 +1162,19 @@ PyFrame_GetBack(PyFrameObject *frame)
11631162
}
11641163

11651164
PyObject*
1166-
_PyEval_BuiltinsFromGlobals(PyObject *globals)
1165+
_PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals)
11671166
{
11681167
PyObject *builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
11691168
if (builtins) {
11701169
if (PyModule_Check(builtins)) {
11711170
builtins = PyModule_GetDict(builtins);
11721171
assert(builtins != NULL);
11731172
}
1174-
return Py_NewRef(builtins);
1173+
return builtins;
11751174
}
1176-
11771175
if (PyErr_Occurred()) {
11781176
return NULL;
11791177
}
11801178

1181-
/* No builtins! Make up a minimal one. Give them 'None', at least. */
1182-
builtins = PyDict_New();
1183-
if (builtins == NULL) {
1184-
return NULL;
1185-
}
1186-
if (PyDict_SetItemString(builtins, "None", Py_None) < 0) {
1187-
Py_DECREF(builtins);
1188-
return NULL;
1189-
}
1190-
return builtins;
1179+
return _PyEval_GetBuiltins(tstate);
11911180
}

Objects/funcobject.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Python.h"
55
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
66
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
7+
#include "pycore_pyerrors.h" // _PyErr_Occurred()
78
#include "structmember.h" // PyMemberDef
89

910
PyObject *
@@ -13,6 +14,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
1314
assert(PyDict_Check(globals));
1415
Py_INCREF(globals);
1516

17+
PyThreadState *tstate = _PyThreadState_GET();
18+
1619
PyCodeObject *code_obj = (PyCodeObject *)code;
1720
Py_INCREF(code_obj);
1821

@@ -42,15 +45,16 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
4245
_Py_IDENTIFIER(__name__);
4346
PyObject *module = _PyDict_GetItemIdWithError(globals, &PyId___name__);
4447
PyObject *builtins = NULL;
45-
if (module == NULL && PyErr_Occurred()) {
48+
if (module == NULL && _PyErr_Occurred(tstate)) {
4649
goto error;
4750
}
4851
Py_XINCREF(module);
4952

50-
builtins = _PyEval_BuiltinsFromGlobals(globals);
53+
builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);
5154
if (builtins == NULL) {
5255
goto error;
5356
}
57+
Py_INCREF(builtins);
5458

5559
PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
5660
if (op == NULL) {

Python/ceval.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -889,10 +889,11 @@ static int unpack_iterable(PyThreadState *, PyObject *, int, int, PyObject **);
889889
PyObject *
890890
PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
891891
{
892+
PyThreadState *tstate = PyThreadState_GET();
892893
if (locals == NULL) {
893894
locals = globals;
894895
}
895-
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
896+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);
896897
if (builtins == NULL) {
897898
return NULL;
898899
}
@@ -906,10 +907,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
906907
.fc_kwdefaults = NULL,
907908
.fc_closure = NULL
908909
};
909-
PyThreadState *tstate = PyThreadState_GET();
910-
PyObject *res = _PyEval_Vector(tstate, &desc, locals, NULL, 0, NULL);
911-
Py_DECREF(builtins);
912-
return res;
910+
return _PyEval_Vector(tstate, &desc, locals, NULL, 0, NULL);
913911
}
914912

915913

@@ -4733,12 +4731,13 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
47334731
PyObject *const *defs, int defcount,
47344732
PyObject *kwdefs, PyObject *closure)
47354733
{
4734+
PyThreadState *tstate = _PyThreadState_GET();
47364735
PyObject *res;
47374736
PyObject *defaults = _PyTuple_FromArray(defs, defcount);
47384737
if (defaults == NULL) {
47394738
return NULL;
47404739
}
4741-
PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals);
4740+
PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals);
47424741
if (builtins == NULL) {
47434742
Py_DECREF(defaults);
47444743
return NULL;
@@ -4797,7 +4796,6 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
47974796
.fc_kwdefaults = kwdefs,
47984797
.fc_closure = closure
47994798
};
4800-
PyThreadState *tstate = _PyThreadState_GET();
48014799
res = _PyEval_Vector(tstate, &constr, locals,
48024800
allargs, argcount,
48034801
kwnames);
@@ -5315,15 +5313,21 @@ PyEval_GetFrame(void)
53155313
return tstate->frame;
53165314
}
53175315

5316+
PyObject *
5317+
_PyEval_GetBuiltins(PyThreadState *tstate)
5318+
{
5319+
PyFrameObject *frame = tstate->frame;
5320+
if (frame != NULL) {
5321+
return frame->f_builtins;
5322+
}
5323+
return tstate->interp->builtins;
5324+
}
5325+
53185326
PyObject *
53195327
PyEval_GetBuiltins(void)
53205328
{
53215329
PyThreadState *tstate = _PyThreadState_GET();
5322-
PyFrameObject *current_frame = tstate->frame;
5323-
if (current_frame == NULL)
5324-
return tstate->interp->builtins;
5325-
else
5326-
return current_frame->f_builtins;
5330+
return _PyEval_GetBuiltins(tstate);
53275331
}
53285332

53295333
/* Convenience function to get a builtin from its name */

0 commit comments

Comments
 (0)