summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_builtin.py33
-rw-r--r--Misc/NEWS5
-rw-r--r--Objects/frameobject.c6
-rw-r--r--Python/ceval.c142
4 files changed, 134 insertions, 52 deletions
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 88bccd9..dfe64bf 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -554,6 +554,39 @@ class BuiltinTest(unittest.TestCase):
del l['__builtins__']
self.assertEqual((g, l), ({'a': 1}, {'b': 2}))
+ def test_exec_globals(self):
+ code = compile("print('Hello World!')", "", "exec")
+ # no builtin function
+ self.assertRaisesRegex(NameError, "name 'print' is not defined",
+ exec, code, {'__builtins__': {}})
+ # __builtins__ must be a mapping type
+ self.assertRaises(TypeError,
+ exec, code, {'__builtins__': 123})
+
+ # no __build_class__ function
+ code = compile("class A: pass", "", "exec")
+ self.assertRaisesRegex(NameError, "__build_class__ not found",
+ exec, code, {'__builtins__': {}})
+
+ class frozendict_error(Exception):
+ pass
+
+ class frozendict(dict):
+ def __setitem__(self, key, value):
+ raise frozendict_error("frozendict is readonly")
+
+ # read-only builtins
+ frozen_builtins = frozendict(__builtins__)
+ code = compile("__builtins__['superglobal']=2; print(superglobal)", "test", "exec")
+ self.assertRaises(frozendict_error,
+ exec, code, {'__builtins__': frozen_builtins})
+
+ # read-only globals
+ namespace = frozendict({})
+ code = compile("x=1", "test", "exec")
+ self.assertRaises(frozendict_error,
+ exec, code, namespace)
+
def test_exec_redirected(self):
savestdout = sys.stdout
sys.stdout = None # Whatever that cannot flush()
diff --git a/Misc/NEWS b/Misc/NEWS
index 595136d..598b9a8 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,11 @@ What's New in Python 3.3.0 Alpha 3?
Core and Builtins
-----------------
+- Issue #14385: 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__.
+
- Issue #12599: Be more strict in accepting None compared to a false-like
object for importlib.util.module_for_loader and
importlib.machinery.PathFinder.
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index c8b8b1d..6208556 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -614,10 +614,8 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
if (builtins) {
if (PyModule_Check(builtins)) {
builtins = PyModule_GetDict(builtins);
- assert(!builtins || PyDict_Check(builtins));
+ assert(builtins != NULL);
}
- else if (!PyDict_Check(builtins))
- builtins = NULL;
}
if (builtins == NULL) {
/* No builtins! Make up a minimal one
@@ -636,7 +634,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
/* If we share the globals, we share the builtins.
Save a lookup and a call. */
builtins = back->f_builtins;
- assert(builtins != NULL && PyDict_Check(builtins));
+ assert(builtins != NULL);
Py_INCREF(builtins);
}
if (code->co_zombieframe != NULL) {
diff --git a/Python/ceval.c b/Python/ceval.c
index 7908d44..a32d685 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1932,11 +1932,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(LOAD_BUILD_CLASS)
{
_Py_IDENTIFIER(__build_class__);
- x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
- if (x == NULL) {
- PyErr_SetString(PyExc_ImportError,
- "__build_class__ not found");
- break;
+
+ if (PyDict_CheckExact(f->f_builtins)) {
+ x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
+ if (x == NULL) {
+ PyErr_SetString(PyExc_NameError,
+ "__build_class__ not found");
+ break;
+ }
+ }
+ else {
+ PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__);
+ if (build_class_str == NULL)
+ break;
+ x = PyObject_GetItem(f->f_builtins, build_class_str);
+ if (x == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_NameError,
+ "__build_class__ not found");
+ break;
+ }
}
Py_INCREF(x);
PUSH(x);
@@ -2078,12 +2093,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
if (x == NULL) {
x = PyDict_GetItem(f->f_globals, w);
if (x == NULL) {
- x = PyDict_GetItem(f->f_builtins, w);
- if (x == NULL) {
- format_exc_check_arg(
- PyExc_NameError,
- NAME_ERROR_MSG, w);
- break;
+ if (PyDict_CheckExact(f->f_builtins)) {
+ x = PyDict_GetItem(f->f_builtins, w);
+ if (x == NULL) {
+ format_exc_check_arg(
+ PyExc_NameError,
+ NAME_ERROR_MSG, w);
+ break;
+ }
+ }
+ else {
+ x = PyObject_GetItem(f->f_builtins, w);
+ if (x == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError))
+ format_exc_check_arg(
+ PyExc_NameError,
+ NAME_ERROR_MSG, w);
+ break;
+ }
}
}
Py_INCREF(x);
@@ -2093,50 +2120,69 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(LOAD_GLOBAL)
w = GETITEM(names, oparg);
- if (PyUnicode_CheckExact(w)) {
- /* Inline the PyDict_GetItem() calls.
- WARNING: this is an extreme speed hack.
- Do not try this at home. */
- Py_hash_t hash = ((PyASCIIObject *)w)->hash;
- if (hash != -1) {
- PyDictObject *d;
- PyDictEntry *e;
- d = (PyDictObject *)(f->f_globals);
- e = d->ma_lookup(d, w, hash);
- if (e == NULL) {
- x = NULL;
- break;
- }
- x = e->me_value;
- if (x != NULL) {
- Py_INCREF(x);
- PUSH(x);
- DISPATCH();
+ if (PyDict_CheckExact(f->f_globals)
+ && PyDict_CheckExact(f->f_builtins)) {
+ if (PyUnicode_CheckExact(w)) {
+ /* Inline the PyDict_GetItem() calls.
+ WARNING: this is an extreme speed hack.
+ Do not try this at home. */
+ Py_hash_t hash = ((PyASCIIObject *)w)->hash;
+ if (hash != -1) {
+ PyDictObject *d;
+ PyDictEntry *e;
+ d = (PyDictObject *)(f->f_globals);
+ e = d->ma_lookup(d, w, hash);
+ if (e == NULL) {
+ x = NULL;
+ break;
+ }
+ x = e->me_value;
+ if (x != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ DISPATCH();
+ }
+ d = (PyDictObject *)(f->f_builtins);
+ e = d->ma_lookup(d, w, hash);
+ if (e == NULL) {
+ x = NULL;
+ break;
+ }
+ x = e->me_value;
+ if (x != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ DISPATCH();
+ }
+ goto load_global_error;
}
- d = (PyDictObject *)(f->f_builtins);
- e = d->ma_lookup(d, w, hash);
- if (e == NULL) {
- x = NULL;
+ }
+ /* This is the un-inlined version of the code above */
+ x = PyDict_GetItem(f->f_globals, w);
+ if (x == NULL) {
+ x = PyDict_GetItem(f->f_builtins, w);
+ if (x == NULL) {
+ load_global_error:
+ format_exc_check_arg(
+ PyExc_NameError,
+ GLOBAL_NAME_ERROR_MSG, w);
break;
}
- x = e->me_value;
- if (x != NULL) {
- Py_INCREF(x);
- PUSH(x);
- DISPATCH();
- }
- goto load_global_error;
}
+ Py_INCREF(x);
+ PUSH(x);
+ DISPATCH();
}
- /* This is the un-inlined version of the code above */
- x = PyDict_GetItem(f->f_globals, w);
+
+ /* Slow-path if globals or builtins is not a dict */
+ x = PyObject_GetItem(f->f_globals, w);
if (x == NULL) {
- x = PyDict_GetItem(f->f_builtins, w);
+ x = PyObject_GetItem(f->f_builtins, w);
if (x == NULL) {
- load_global_error:
- format_exc_check_arg(
- PyExc_NameError,
- GLOBAL_NAME_ERROR_MSG, w);
+ if (PyErr_ExceptionMatches(PyExc_KeyError))
+ format_exc_check_arg(
+ PyExc_NameError,
+ GLOBAL_NAME_ERROR_MSG, w);
break;
}
}