summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_types.py22
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst2
-rw-r--r--Python/bltinmodule.c7
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));