[Python-checkins] cpython: Issue #14385: Support other types than dict for __builtins__

victor.stinner python-checkins at python.org
Thu Apr 19 01:00:19 CEST 2012


http://hg.python.org/cpython/rev/e3ab8aa0216c
changeset:   76411:e3ab8aa0216c
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Apr 19 00:57:45 2012 +0200
summary:
  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__.

files:
  Lib/test/test_builtin.py |   33 ++++++
  Misc/NEWS                |    5 +
  Objects/frameobject.c    |    6 +-
  Python/ceval.c           |  144 +++++++++++++++++---------
  4 files changed, 135 insertions(+), 53 deletions(-)


diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -554,6 +554,39 @@
             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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,11 @@
 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
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -614,10 +614,8 @@
         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 @@
         /* 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
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1932,11 +1932,26 @@
         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 @@
             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 @@
 
         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;
+            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;
+                    }
+                }
+                /* 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();
-                    }
-                    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;
                 }
+                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;
                 }
             }

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list