diff options
-rw-r--r-- | Lib/test/test_types.py | 22 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 2 | ||||
-rw-r--r-- | Python/bltinmodule.c | 7 |
3 files changed, 31 insertions, 0 deletions
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 3fd66db..966ef6d 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -864,6 +864,28 @@ class ClassCreationTests(unittest.TestCase): self.assertIs(ns, expected_ns) 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): # issue1294232: correct metaclass calculation new_calls = [] # to check the order of __new__ calls diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst b/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst new file mode 100644 index 0000000..44a7aba --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst @@ -0,0 +1,2 @@ +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. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index c363cfe..2269fe2 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -157,6 +157,13 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs, Py_DECREF(bases); 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, NULL, 0, NULL, 0, NULL, 0, NULL, PyFunction_GET_CLOSURE(func)); |