mirror of
https://github.com/python/cpython.git
synced 2025-09-13 20:27:05 +00:00
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__.
This commit is contained in:
parent
05fac022ec
commit
b0b224233e
4 changed files with 138 additions and 56 deletions
|
@ -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?
|
||||||
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,
|
||||||
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,
|
||||||
/* 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,12 +1932,27 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
TARGET(LOAD_BUILD_CLASS)
|
TARGET(LOAD_BUILD_CLASS)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(__build_class__);
|
_Py_IDENTIFIER(__build_class__);
|
||||||
|
|
||||||
|
if (PyDict_CheckExact(f->f_builtins)) {
|
||||||
x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
|
x = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
|
||||||
if (x == NULL) {
|
if (x == NULL) {
|
||||||
PyErr_SetString(PyExc_ImportError,
|
PyErr_SetString(PyExc_NameError,
|
||||||
"__build_class__ not found");
|
"__build_class__ not found");
|
||||||
break;
|
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);
|
||||||
break;
|
break;
|
||||||
|
@ -2078,6 +2093,7 @@ 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) {
|
||||||
|
if (PyDict_CheckExact(f->f_builtins)) {
|
||||||
x = PyDict_GetItem(f->f_builtins, w);
|
x = PyDict_GetItem(f->f_builtins, w);
|
||||||
if (x == NULL) {
|
if (x == NULL) {
|
||||||
format_exc_check_arg(
|
format_exc_check_arg(
|
||||||
|
@ -2086,6 +2102,17 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
break;
|
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);
|
||||||
}
|
}
|
||||||
PUSH(x);
|
PUSH(x);
|
||||||
|
@ -2093,6 +2120,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
|
|
||||||
TARGET(LOAD_GLOBAL)
|
TARGET(LOAD_GLOBAL)
|
||||||
w = GETITEM(names, oparg);
|
w = GETITEM(names, oparg);
|
||||||
|
if (PyDict_CheckExact(f->f_globals)
|
||||||
|
&& PyDict_CheckExact(f->f_builtins)) {
|
||||||
if (PyUnicode_CheckExact(w)) {
|
if (PyUnicode_CheckExact(w)) {
|
||||||
/* Inline the PyDict_GetItem() calls.
|
/* Inline the PyDict_GetItem() calls.
|
||||||
WARNING: this is an extreme speed hack.
|
WARNING: this is an extreme speed hack.
|
||||||
|
@ -2143,6 +2172,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
Py_INCREF(x);
|
Py_INCREF(x);
|
||||||
PUSH(x);
|
PUSH(x);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slow-path if globals or builtins is not a dict */
|
||||||
|
x = PyObject_GetItem(f->f_globals, w);
|
||||||
|
if (x == NULL) {
|
||||||
|
x = PyObject_GetItem(f->f_builtins, w);
|
||||||
|
if (x == NULL) {
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_KeyError))
|
||||||
|
format_exc_check_arg(
|
||||||
|
PyExc_NameError,
|
||||||
|
GLOBAL_NAME_ERROR_MSG, w);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_INCREF(x);
|
||||||
|
PUSH(x);
|
||||||
|
DISPATCH();
|
||||||
|
|
||||||
TARGET(DELETE_FAST)
|
TARGET(DELETE_FAST)
|
||||||
x = GETLOCAL(oparg);
|
x = GETLOCAL(oparg);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue