Commit b0b22423 authored by Victor Stinner's avatar Victor Stinner

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__.
parent 05fac022
...@@ -554,6 +554,39 @@ class BuiltinTest(unittest.TestCase): ...@@ -554,6 +554,39 @@ class BuiltinTest(unittest.TestCase):
del l['__builtins__'] del l['__builtins__']
self.assertEqual((g, l), ({'a': 1}, {'b': 2})) 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): def test_exec_redirected(self):
savestdout = sys.stdout savestdout = sys.stdout
sys.stdout = None # Whatever that cannot flush() sys.stdout = None # Whatever that cannot flush()
......
...@@ -10,6 +10,11 @@ What's New in Python 3.3.0 Alpha 3? ...@@ -10,6 +10,11 @@ What's New in Python 3.3.0 Alpha 3?
Core and Builtins 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 - Issue #12599: Be more strict in accepting None compared to a false-like
object for importlib.util.module_for_loader and object for importlib.util.module_for_loader and
importlib.machinery.PathFinder. importlib.machinery.PathFinder.
......
...@@ -614,10 +614,8 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, ...@@ -614,10 +614,8 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
if (builtins) { if (builtins) {
if (PyModule_Check(builtins)) { if (PyModule_Check(builtins)) {
builtins = PyModule_GetDict(builtins); builtins = PyModule_GetDict(builtins);
assert(!builtins || PyDict_Check(builtins)); assert(builtins != NULL);
} }
else if (!PyDict_Check(builtins))
builtins = NULL;
} }
if (builtins == NULL) { if (builtins == NULL) {
/* No builtins! Make up a minimal one /* No builtins! Make up a minimal one
...@@ -636,7 +634,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, ...@@ -636,7 +634,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
/* If we share the globals, we share the builtins. /* If we share the globals, we share the builtins.
Save a lookup and a call. */ Save a lookup and a call. */
builtins = back->f_builtins; builtins = back->f_builtins;
assert(builtins != NULL && PyDict_Check(builtins)); assert(builtins != NULL);
Py_INCREF(builtins); Py_INCREF(builtins);
} }
if (code->co_zombieframe != NULL) { if (code->co_zombieframe != NULL) {
......
...@@ -1932,11 +1932,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -1932,11 +1932,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(LOAD_BUILD_CLASS) TARGET(LOAD_BUILD_CLASS)
{ {
_Py_IDENTIFIER(__build_class__); _Py_IDENTIFIER(__build_class__);
x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
if (x == NULL) { if (PyDict_CheckExact(f->f_builtins)) {
PyErr_SetString(PyExc_ImportError, x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
"__build_class__ not found"); if (x == NULL) {
break; 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); Py_INCREF(x);
PUSH(x); PUSH(x);
...@@ -2078,12 +2093,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2078,12 +2093,24 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
if (x == NULL) { if (x == NULL) {
x = PyDict_GetItem(f->f_globals, w); x = PyDict_GetItem(f->f_globals, w);
if (x == NULL) { if (x == NULL) {
x = PyDict_GetItem(f->f_builtins, w); if (PyDict_CheckExact(f->f_builtins)) {
if (x == NULL) { x = PyDict_GetItem(f->f_builtins, w);
format_exc_check_arg( if (x == NULL) {
PyExc_NameError, format_exc_check_arg(
NAME_ERROR_MSG, w); PyExc_NameError,
break; 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); Py_INCREF(x);
...@@ -2093,50 +2120,69 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2093,50 +2120,69 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(LOAD_GLOBAL) TARGET(LOAD_GLOBAL)
w = GETITEM(names, oparg); w = GETITEM(names, oparg);
if (PyUnicode_CheckExact(w)) { if (PyDict_CheckExact(f->f_globals)
/* Inline the PyDict_GetItem() calls. && PyDict_CheckExact(f->f_builtins)) {
WARNING: this is an extreme speed hack. if (PyUnicode_CheckExact(w)) {
Do not try this at home. */ /* Inline the PyDict_GetItem() calls.
Py_hash_t hash = ((PyASCIIObject *)w)->hash; WARNING: this is an extreme speed hack.
if (hash != -1) { Do not try this at home. */
PyDictObject *d; Py_hash_t hash = ((PyASCIIObject *)w)->hash;
PyDictEntry *e; if (hash != -1) {
d = (PyDictObject *)(f->f_globals); PyDictObject *d;
e = d->ma_lookup(d, w, hash); PyDictEntry *e;
if (e == NULL) { d = (PyDictObject *)(f->f_globals);
x = NULL; e = d->ma_lookup(d, w, hash);
break; if (e == NULL) {
} x = NULL;
x = e->me_value; break;
if (x != NULL) { }
Py_INCREF(x); x = e->me_value;
PUSH(x); if (x != NULL) {
DISPATCH(); 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); /* This is the un-inlined version of the code above */
if (e == NULL) { x = PyDict_GetItem(f->f_globals, w);
x = NULL; 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; 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) { if (x == NULL) {
x = PyDict_GetItem(f->f_builtins, w); x = PyObject_GetItem(f->f_builtins, w);
if (x == NULL) { if (x == NULL) {
load_global_error: if (PyErr_ExceptionMatches(PyExc_KeyError))
format_exc_check_arg( format_exc_check_arg(
PyExc_NameError, PyExc_NameError,
GLOBAL_NAME_ERROR_MSG, w); GLOBAL_NAME_ERROR_MSG, w);
break; break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment