From 58df0812beb316ba6e05f4818babb36e983dc06c Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 26 Sep 2017 10:41:45 +0300 Subject: [PATCH 1/8] init commit --- Lib/test/test_types.py | 9 +++++++++ Python/bltinmodule.c | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 3fd66dbc70d8519..483e0ad5edc5408 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -864,6 +864,15 @@ def __prepare__(*args): self.assertIs(ns, expected_ns) self.assertEqual(len(kwds), 0) + def test_bad___prepare__(self): + # __prepare__() must return a mapping. + class BadMetaclass(type): + def __prepare__(*args): + pass + with self.assertRaises(TypeError): + class Foo(metaclass=BadMetaclass): + pass + def test_metaclass_derivation(self): # issue1294232: correct metaclass calculation new_calls = [] # to check the order of __new__ calls diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index c363cfe8cea26bd..2269fe21657e61c 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 : "", + 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)); From bff6f258675badce585d0b7a9cba7912f6dad94e Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 26 Sep 2017 13:15:09 +0300 Subject: [PATCH 2/8] added a NEWS.d item, and added a test --- Lib/test/test_types.py | 6 ++++++ .../2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 483e0ad5edc5408..2600daba95f13a9 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -872,6 +872,12 @@ def __prepare__(*args): with self.assertRaises(TypeError): class Foo(metaclass=BadMetaclass): pass + class BadMetaclass: + def __prepare__(*args): + pass + with self.assertRaises(TypeError): + class Bar(metaclass=BadMetaclass()): + pass def test_metaclass_derivation(self): # issue1294232: correct metaclass calculation 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 000000000000000..9f6edff25d9a2b2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst @@ -0,0 +1,2 @@ +Prevent raising a SystemError in class creation in case of a metaclass with +a bad ``__prepare__()`` method. Patch by Oren Milman. From 6c7ce6b4be7a581f5084bcd583576893951ff247 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 26 Sep 2017 14:12:46 +0300 Subject: [PATCH 3/8] cosmetic improvements to the test. --- Lib/test/test_types.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 2600daba95f13a9..bab209836ad9abb 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -866,17 +866,18 @@ def __prepare__(*args): def test_bad___prepare__(self): # __prepare__() must return a mapping. - class BadMetaclass(type): + class BadMeta(type): def __prepare__(*args): pass with self.assertRaises(TypeError): - class Foo(metaclass=BadMetaclass): + class Foo(metaclass=BadMeta): pass - class BadMetaclass: + # Also test the case in which metaclass is not a type. + class BadMeta: def __prepare__(*args): pass with self.assertRaises(TypeError): - class Bar(metaclass=BadMetaclass()): + class Bar(metaclass=BadMeta()): pass def test_metaclass_derivation(self): From bf6f425c1d9e14eadbb343806abaae58a5e111ca Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 26 Sep 2017 14:14:18 +0300 Subject: [PATCH 4/8] improve a comment. --- Lib/test/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index bab209836ad9abb..e9ef8b25233bdb2 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -872,7 +872,7 @@ def __prepare__(*args): with self.assertRaises(TypeError): class Foo(metaclass=BadMeta): pass - # Also test the case in which metaclass is not a type. + # Also test the case in which the metaclass is not a type. class BadMeta: def __prepare__(*args): pass From 1571f3b514fa73e565e31ebcbfaf8df9931f608f Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 26 Sep 2017 18:06:48 +0300 Subject: [PATCH 5/8] improve the test and the NEWS.d item --- Lib/test/test_types.py | 4 ++-- .../2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index e9ef8b25233bdb2..288e9db7706db51 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -868,14 +868,14 @@ def test_bad___prepare__(self): # __prepare__() must return a mapping. class BadMeta(type): def __prepare__(*args): - pass + return None with self.assertRaises(TypeError): class Foo(metaclass=BadMeta): pass # Also test the case in which the metaclass is not a type. class BadMeta: def __prepare__(*args): - pass + return None with self.assertRaises(TypeError): class Bar(metaclass=BadMeta()): pass 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 index 9f6edff25d9a2b2..c9c9bf707622371 100644 --- 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 @@ -1,2 +1,2 @@ -Prevent raising a SystemError in class creation in case of a metaclass with -a bad ``__prepare__()`` method. Patch by Oren Milman. +Raise a `TypeError` instead of `SystemError` in case class creation failed due +to a metaclass with a bad ``__prepare__()`` method. Patch by Oren Milman. From 1462735928ee2e2c7a699659bdd2352b55f6fa49 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Wed, 27 Sep 2017 14:40:57 +0300 Subject: [PATCH 6/8] improve the tests --- Lib/test/test_types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 288e9db7706db51..966ef6d8782575c 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -867,16 +867,22 @@ def __prepare__(*args): def test_bad___prepare__(self): # __prepare__() must return a mapping. class BadMeta(type): + @classmethod def __prepare__(*args): return None - with self.assertRaises(TypeError): + 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.assertRaises(TypeError): + with self.assertRaisesRegex(TypeError, + r'^\.__prepare__\(\) must ' + r'return a mapping, not NoneType$'): class Bar(metaclass=BadMeta()): pass From 7f70a1ed929fc9f288d66272f5f56aaf7eb0d055 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Wed, 27 Sep 2017 14:55:44 +0300 Subject: [PATCH 7/8] fix the NEWS.d item --- .../2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index c9c9bf707622371..97ca9bd0f0e2a5b 100644 --- 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 @@ -1,2 +1,2 @@ -Raise a `TypeError` instead of `SystemError` in case class creation failed due -to a metaclass with a bad ``__prepare__()`` method. Patch by Oren Milman. +Raise a `TypeError` with a helpful error message in case class creation failed +due to a metaclass with a bad ``__prepare__()`` method. Patch by Oren Milman. From dcd522707817420a45450d217e5de807f41490bd Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Wed, 27 Sep 2017 23:26:42 +1000 Subject: [PATCH 8/8] Minor grammar fix --- .../Core and Builtins/2017-09-26-13-03-16.bpo-31588.wT9Iy7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 97ca9bd0f0e2a5b..44a7aba9d7f94a8 100644 --- 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 @@ -1,2 +1,2 @@ -Raise a `TypeError` with a helpful error message in case class creation failed +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.