Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b0b2242

Browse files
committed
Issue #14385: Support other types than dict for __builtins__
It is now possible to use a custom type for the __builtins__ namespace, instead of a dict. It can be used for sandboxing for example. Raise also a NameError instead of ImportError if __build_class__ name if not found in __builtins__.
1 parent 05fac02 commit b0b2242

4 files changed

Lines changed: 134 additions & 52 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,39 @@ def test_exec(self):
554554
del l['__builtins__']
555555
self.assertEqual((g, l), ({'a': 1}, {'b': 2}))
556556

557+
def test_exec_globals(self):
558+
code = compile("print('Hello World!')", "", "exec")
559+
# no builtin function
560+
self.assertRaisesRegex(NameError, "name 'print' is not defined",
561+
exec, code, {'__builtins__': {}})
562+
# __builtins__ must be a mapping type
563+
self.assertRaises(TypeError,
564+
exec, code, {'__builtins__': 123})
565+
566+
# no __build_class__ function
567+
code = compile("class A: pass", "", "exec")
568+
self.assertRaisesRegex(NameError, "__build_class__ not found",
569+
exec, code, {'__builtins__': {}})
570+
571+
class frozendict_error(Exception):
572+
pass
573+
574+
class frozendict(dict):
575+
def __setitem__(self, key, value):
576+
raise frozendict_error("frozendict is readonly")
577+
578+
# read-only builtins
579+
frozen_builtins = frozendict(__builtins__)
580+
code = compile("__builtins__['superglobal']=2; print(superglobal)", "test", "exec")
581+
self.assertRaises(frozendict_error,
582+
exec, code, {'__builtins__': frozen_builtins})
583+
584+
# read-only globals
585+
namespace = frozendict({})
586+
code = compile("x=1", "test", "exec")
587+
self.assertRaises(frozendict_error,
588+
exec, code, namespace)
589+
557590
def test_exec_redirected(self):
558591
savestdout = sys.stdout
559592
sys.stdout = None # Whatever that cannot flush()

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ What's New in Python 3.3.0 Alpha 3?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #14385: It is now possible to use a custom type for the __builtins__
14+
namespace, instead of a dict. It can be used for sandboxing for example.
15+
Raise also a NameError instead of ImportError if __build_class__ name if not
16+
found in __builtins__.
17+
1318
- Issue #12599: Be more strict in accepting None compared to a false-like
1419
object for importlib.util.module_for_loader and
1520
importlib.machinery.PathFinder.

Objects/frameobject.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -614,10 +614,8 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
614614
if (builtins) {
615615
if (PyModule_Check(builtins)) {
616616
builtins = PyModule_GetDict(builtins);
617-
assert(!builtins || PyDict_Check(builtins));
617+
assert(builtins != NULL);
618618
}
619-
else if (!PyDict_Check(builtins))
620-
builtins = NULL;
621619
}
622620
if (builtins == NULL) {
623621
/* No builtins! Make up a minimal one
@@ -636,7 +634,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
636634
/* If we share the globals, we share the builtins.
637635
Save a lookup and a call. */
638636
builtins = back->f_builtins;
639-
assert(builtins != NULL && PyDict_Check(builtins));
637+
assert(builtins != NULL);
640638
Py_INCREF(builtins);
641639
}
642640
if (code->co_zombieframe != NULL) {

Python/ceval.c

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,11 +1932,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
19321932
TARGET(LOAD_BUILD_CLASS)
19331933
{
19341934
_Py_IDENTIFIER(__build_class__);
1935-
x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
1936-
if (x == NULL) {
1937-
PyErr_SetString(PyExc_ImportError,
1938-
"__build_class__ not found");
1939-
break;
1935+
1936+
if (PyDict_CheckExact(f->f_builtins)) {
1937+
x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
1938+
if (x == NULL) {
1939+
PyErr_SetString(PyExc_NameError,
1940+
"__build_class__ not found");
1941+
break;
1942+
}
1943+
}
1944+
else {
1945+
PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__);
1946+
if (build_class_str == NULL)
1947+
break;
1948+
x = PyObject_GetItem(f->f_builtins, build_class_str);
1949+
if (x == NULL) {
1950+
if (PyErr_ExceptionMatches(PyExc_KeyError))
1951+
PyErr_SetString(PyExc_NameError,
1952+
"__build_class__ not found");
1953+
break;
1954+
}
19401955
}
19411956
Py_INCREF(x);
19421957
PUSH(x);
@@ -2078,12 +2093,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
20782093
if (x == NULL) {
20792094
x = PyDict_GetItem(f->f_globals, w);
20802095
if (x == NULL) {
2081-
x = PyDict_GetItem(f->f_builtins, w);
2082-
if (x == NULL) {
2083-
format_exc_check_arg(
2084-
PyExc_NameError,
2085-
NAME_ERROR_MSG, w);
2086-
break;
2096+
if (PyDict_CheckExact(f->f_builtins)) {
2097+
x = PyDict_GetItem(f->f_builtins, w);
2098+
if (x == NULL) {
2099+
format_exc_check_arg(
2100+
PyExc_NameError,
2101+
NAME_ERROR_MSG, w);
2102+
break;
2103+
}
2104+
}
2105+
else {
2106+
x = PyObject_GetItem(f->f_builtins, w);
2107+
if (x == NULL) {
2108+
if (PyErr_ExceptionMatches(PyExc_KeyError))
2109+
format_exc_check_arg(
2110+
PyExc_NameError,
2111+
NAME_ERROR_MSG, w);
2112+
break;
2113+
}
20872114
}
20882115
}
20892116
Py_INCREF(x);
@@ -2093,50 +2120,69 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
20932120

20942121
TARGET(LOAD_GLOBAL)
20952122
w = GETITEM(names, oparg);
2096-
if (PyUnicode_CheckExact(w)) {
2097-
/* Inline the PyDict_GetItem() calls.
2098-
WARNING: this is an extreme speed hack.
2099-
Do not try this at home. */
2100-
Py_hash_t hash = ((PyASCIIObject *)w)->hash;
2101-
if (hash != -1) {
2102-
PyDictObject *d;
2103-
PyDictEntry *e;
2104-
d = (PyDictObject *)(f->f_globals);
2105-
e = d->ma_lookup(d, w, hash);
2106-
if (e == NULL) {
2107-
x = NULL;
2108-
break;
2109-
}
2110-
x = e->me_value;
2111-
if (x != NULL) {
2112-
Py_INCREF(x);
2113-
PUSH(x);
2114-
DISPATCH();
2123+
if (PyDict_CheckExact(f->f_globals)
2124+
&& PyDict_CheckExact(f->f_builtins)) {
2125+
if (PyUnicode_CheckExact(w)) {
2126+
/* Inline the PyDict_GetItem() calls.
2127+
WARNING: this is an extreme speed hack.
2128+
Do not try this at home. */
2129+
Py_hash_t hash = ((PyASCIIObject *)w)->hash;
2130+
if (hash != -1) {
2131+
PyDictObject *d;
2132+
PyDictEntry *e;
2133+
d = (PyDictObject *)(f->f_globals);
2134+
e = d->ma_lookup(d, w, hash);
2135+
if (e == NULL) {
2136+
x = NULL;
2137+
break;
2138+
}
2139+
x = e->me_value;
2140+
if (x != NULL) {
2141+
Py_INCREF(x);
2142+
PUSH(x);
2143+
DISPATCH();
2144+
}
2145+
d = (PyDictObject *)(f->f_builtins);
2146+
e = d->ma_lookup(d, w, hash);
2147+
if (e == NULL) {
2148+
x = NULL;
2149+
break;
2150+
}
2151+
x = e->me_value;
2152+
if (x != NULL) {
2153+
Py_INCREF(x);
2154+
PUSH(x);
2155+
DISPATCH();
2156+
}
2157+
goto load_global_error;
21152158
}
2116-
d = (PyDictObject *)(f->f_builtins);
2117-
e = d->ma_lookup(d, w, hash);
2118-
if (e == NULL) {
2119-
x = NULL;
2159+
}
2160+
/* This is the un-inlined version of the code above */
2161+
x = PyDict_GetItem(f->f_globals, w);
2162+
if (x == NULL) {
2163+
x = PyDict_GetItem(f->f_builtins, w);
2164+
if (x == NULL) {
2165+
load_global_error:
2166+
format_exc_check_arg(
2167+
PyExc_NameError,
2168+
GLOBAL_NAME_ERROR_MSG, w);
21202169
break;
21212170
}
2122-
x = e->me_value;
2123-
if (x != NULL) {
2124-
Py_INCREF(x);
2125-
PUSH(x);
2126-
DISPATCH();
2127-
}
2128-
goto load_global_error;
21292171
}
2172+
Py_INCREF(x);
2173+
PUSH(x);
2174+
DISPATCH();
21302175
}
2131-
/* This is the un-inlined version of the code above */
2132-
x = PyDict_GetItem(f->f_globals, w);
2176+
2177+
/* Slow-path if globals or builtins is not a dict */
2178+
x = PyObject_GetItem(f->f_globals, w);
21332179
if (x == NULL) {
2134-
x = PyDict_GetItem(f->f_builtins, w);
2180+
x = PyObject_GetItem(f->f_builtins, w);
21352181
if (x == NULL) {
2136-
load_global_error:
2137-
format_exc_check_arg(
2138-
PyExc_NameError,
2139-
GLOBAL_NAME_ERROR_MSG, w);
2182+
if (PyErr_ExceptionMatches(PyExc_KeyError))
2183+
format_exc_check_arg(
2184+
PyExc_NameError,
2185+
GLOBAL_NAME_ERROR_MSG, w);
21402186
break;
21412187
}
21422188
}

0 commit comments

Comments
 (0)