Kaydet (Commit) 5837d041 authored tarafından Oren Milman's avatar Oren Milman Kaydeden (comit) Nick Coghlan

bpo-31588: Validate return value of __prepare__() methods (GH-3764)

Class execution requires that __prepare__() methods return
a proper execution namespace. Check for that immediately
after calling __prepare__(), rather than passing it through
to the code execution machinery and potentially triggering
SystemError (in debug builds) or a cryptic TypeError
(in release builds).

Patch by Oren Milman.
üst 236329ed
...@@ -864,6 +864,28 @@ class ClassCreationTests(unittest.TestCase): ...@@ -864,6 +864,28 @@ class ClassCreationTests(unittest.TestCase):
self.assertIs(ns, expected_ns) self.assertIs(ns, expected_ns)
self.assertEqual(len(kwds), 0) self.assertEqual(len(kwds), 0)
def test_bad___prepare__(self):
# __prepare__() must return a mapping.
class BadMeta(type):
@classmethod
def __prepare__(*args):
return None
with self.assertRaisesRegex(TypeError,
r'^BadMeta\.__prepare__\(\) must '
r'return a mapping, not NoneType$'):
class Foo(metaclass=BadMeta):
pass
# Also test the case in which the metaclass is not a type.
class BadMeta:
@classmethod
def __prepare__(*args):
return None
with self.assertRaisesRegex(TypeError,
r'^<metaclass>\.__prepare__\(\) must '
r'return a mapping, not NoneType$'):
class Bar(metaclass=BadMeta()):
pass
def test_metaclass_derivation(self): def test_metaclass_derivation(self):
# issue1294232: correct metaclass calculation # issue1294232: correct metaclass calculation
new_calls = [] # to check the order of __new__ calls new_calls = [] # to check the order of __new__ calls
......
Raise a `TypeError` with a helpful error message when class creation fails
due to a metaclass with a bad ``__prepare__()`` method. Patch by Oren Milman.
...@@ -157,6 +157,13 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs, ...@@ -157,6 +157,13 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs,
Py_DECREF(bases); Py_DECREF(bases);
return NULL; return NULL;
} }
if (!PyMapping_Check(ns)) {
PyErr_Format(PyExc_TypeError,
"%.200s.__prepare__() must return a mapping, not %.200s",
isclass ? ((PyTypeObject *)meta)->tp_name : "<metaclass>",
Py_TYPE(ns)->tp_name);
goto error;
}
cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns, cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns,
NULL, 0, NULL, 0, NULL, 0, NULL, NULL, 0, NULL, 0, NULL, 0, NULL,
PyFunction_GET_CLOSURE(func)); PyFunction_GET_CLOSURE(func));
......
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