From e15fc35df11c27e59140231f52c8a0859c661bf9 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 20:12:33 +0900 Subject: [PATCH 01/12] Move identifiers to function local --- Modules/_abc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 504e23d9a74d3e1..2702bb80a3b250f 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -12,13 +12,7 @@ module _abc PyDoc_STRVAR(_abc__doc__, "Module contains faster C implementation of abc.ABCMeta"); -_Py_IDENTIFIER(__abstractmethods__); -_Py_IDENTIFIER(__class__); -_Py_IDENTIFIER(__dict__); -_Py_IDENTIFIER(__bases__); _Py_IDENTIFIER(_abc_impl); -_Py_IDENTIFIER(__subclasscheck__); -_Py_IDENTIFIER(__subclasshook__); /* A global counter that is incremented each time a class is registered as a virtual subclass of anything. It forces the @@ -258,6 +252,7 @@ _abc__get_dump(PyObject *module, PyObject *self) static int compute_abstract_methods(PyObject *self) { + _Py_IDENTIFIER(__abstractmethods__); int ret = -1; PyObject *abstracts = PyFrozenSet_New(NULL); if (abstracts == NULL) { @@ -267,6 +262,7 @@ compute_abstract_methods(PyObject *self) PyObject *ns = NULL, *items = NULL, *bases = NULL; // Py_XDECREF()ed on error. /* Stage 1: direct abstract methods. */ + _Py_IDENTIFIER(__dict__); ns = _PyObject_GetAttrId(self, &PyId___dict__); if (!ns) { goto error; @@ -311,6 +307,7 @@ compute_abstract_methods(PyObject *self) } /* Stage 2: inherited abstract methods. */ + _Py_IDENTIFIER(__bases__); bases = _PyObject_GetAttrId(self, &PyId___bases__); if (!bases) { goto error; @@ -479,12 +476,14 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, PyObject *instance) /*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/ { + _Py_IDENTIFIER(__subclasscheck__); PyObject *subtype, *result = NULL, *subclass = NULL; _abc_data *impl = _get_impl(self); if (impl == NULL) { return NULL; } + _Py_IDENTIFIER(__class__); subclass = _PyObject_GetAttrId(instance, &PyId___class__); if (subclass == NULL) { Py_DECREF(impl); @@ -608,6 +607,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, } /* 3. Check the subclass hook. */ + _Py_IDENTIFIER(__subclasshook__); ok = _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId___subclasshook__, subclass, NULL); if (ok == NULL) { From 868fa3a8b544c52840f17ef2def349a250a2e9e5 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 20:12:56 +0900 Subject: [PATCH 02/12] add test --- Lib/test/test_abc.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index af26c1d6b8c5ea6..ff1c93e0c3402c4 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -392,6 +392,13 @@ class MyInt(int): self.assertIsInstance(42, A) self.assertIsInstance(42, (A,)) + def test_issubclass(self): + class A(metaclass=abc_ABCMeta): + pass + with self.assertRaises(TypeError): + issubclass(42, A) + + def test_all_new_methods_are_called(self): class A(metaclass=abc_ABCMeta): pass From 97ec4e7a11e4a79cba6f4ac3f179501f39c5ab06 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 20:24:24 +0900 Subject: [PATCH 03/12] fix the bug --- Modules/_abc.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 2702bb80a3b250f..e0d89eec1894618 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -637,20 +637,29 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, } Py_DECREF(ok); - /* 4. Check if it's a direct subclass. */ - mro = ((PyTypeObject *)subclass)->tp_mro; - assert(PyTuple_Check(mro)); - for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) { - PyObject *mro_item = PyTuple_GET_ITEM(mro, pos); - if (mro_item == NULL) { - goto end; - } - if ((PyObject *)self == mro_item) { - if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) { + /* 4. Check if it's a direct subclass. + * + * if cls in getattr(subclass, '__mro__', ()): + * cls._abc_cache.add(subclass) + * return True + */ + _Py_IDENTIFIER(__mro__); + if (_PyObject_LookupAttrId(subclass, &PyId___mro__, &mro) < 0) { + return NULL; + } + if (mro != NULL && PyTuple_Check(mro)) { + for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) { + PyObject *mro_item = PyTuple_GET_ITEM(mro, pos); + if (mro_item == NULL) { + goto end; + } + if ((PyObject *)self == mro_item) { + if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) { + goto end; + } + result = Py_True; goto end; } - result = Py_True; - goto end; } } From 59998e7e569dbe2ae2906a9fef5f8480a6c7bbcb Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 20:30:23 +0900 Subject: [PATCH 04/12] Add NEWS --- .../next/Library/2018-03-06-20-30-20.bpo-32999.lgFXWl.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-03-06-20-30-20.bpo-32999.lgFXWl.rst diff --git a/Misc/NEWS.d/next/Library/2018-03-06-20-30-20.bpo-32999.lgFXWl.rst b/Misc/NEWS.d/next/Library/2018-03-06-20-30-20.bpo-32999.lgFXWl.rst new file mode 100644 index 000000000000000..45e75f939310c6b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-03-06-20-30-20.bpo-32999.lgFXWl.rst @@ -0,0 +1,2 @@ +Fix C implemetation of ``ABC.__subclasscheck__(cls, subclass)`` crashed when +``subclass`` is not a type object. From b901e1e6315a90a82d4e22e8ced2c279acc58643 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 21:31:48 +0900 Subject: [PATCH 05/12] Fix leak --- Modules/_abc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index e0d89eec1894618..1162758adc8210f 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -567,7 +567,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, PyObject *subclass) /*[clinic end generated code: output=b56c9e4a530e3894 input=1d947243409d10b8]*/ { - PyObject *ok, *mro, *subclasses = NULL, *result = NULL; + PyObject *ok, *mro = NULL, *subclasses = NULL, *result = NULL; Py_ssize_t pos; int incache; _abc_data *impl = _get_impl(self); @@ -645,7 +645,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, */ _Py_IDENTIFIER(__mro__); if (_PyObject_LookupAttrId(subclass, &PyId___mro__, &mro) < 0) { - return NULL; + goto end; } if (mro != NULL && PyTuple_Check(mro)) { for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) { @@ -699,6 +699,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, result = Py_False; end: + Py_XDECREF(mro); Py_XDECREF(impl); Py_XDECREF(subclasses); Py_XINCREF(result); From c06fcaea1c1947b8a3841dd7d86d363d9f5ac62c Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 21:52:24 +0900 Subject: [PATCH 06/12] More corner cases --- Lib/test/test_abc.py | 13 ++++++++++++- Modules/_abc.c | 8 +++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index ff1c93e0c3402c4..21c7459360b93fb 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -395,9 +395,20 @@ class MyInt(int): def test_issubclass(self): class A(metaclass=abc_ABCMeta): pass + + with self.assertRaises(TypeError): + issubclass({}, A) # unhashable + with self.assertRaises(TypeError): - issubclass(42, A) + issubclass(42, A) # No __mro__ + # Python version supports any iterable as __mro__. + # But it's implementation detail and don't emulate it in C version. + class C: + __mro__ = 42 # __mro__ is not tuple + + with self.assertRaises(TypeError): + self.assertTrue(issubclass(C(), A)) def test_all_new_methods_are_called(self): class A(metaclass=abc_ABCMeta): diff --git a/Modules/_abc.c b/Modules/_abc.c index 1162758adc8210f..26636db61c53689 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -647,7 +647,13 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, if (_PyObject_LookupAttrId(subclass, &PyId___mro__, &mro) < 0) { goto end; } - if (mro != NULL && PyTuple_Check(mro)) { + if (mro != NULL) { + if (!PyTuple_Check(mro)) { + // Python version supports non-tuple iterable. Keep it as + // implementation detail. + PyErr_SetString(PyExc_TypeError, "__mro__ is not a tuple"); + goto end; + } for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) { PyObject *mro_item = PyTuple_GET_ITEM(mro, pos); if (mro_item == NULL) { From 7767ef775f7ea9dc781c0545972ef34333efa7ac Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 21:54:12 +0900 Subject: [PATCH 07/12] impl must not be NULL --- Modules/_abc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 26636db61c53689..cea4582db28470d 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -705,8 +705,8 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, result = Py_False; end: + Py_DECREF(impl); Py_XDECREF(mro); - Py_XDECREF(impl); Py_XDECREF(subclasses); Py_XINCREF(result); return result; From e2aa5cad681f08fd50009eea3c4cfe69b4ed61d6 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 6 Mar 2018 22:15:35 +0900 Subject: [PATCH 08/12] oops --- Lib/test/test_abc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 21c7459360b93fb..a65d44d2645a638 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -408,7 +408,7 @@ class C: __mro__ = 42 # __mro__ is not tuple with self.assertRaises(TypeError): - self.assertTrue(issubclass(C(), A)) + issubclass(C(), A) def test_all_new_methods_are_called(self): class A(metaclass=abc_ABCMeta): From 23f555f072443df42e592ab61e9e10e6b249b57c Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 7 Mar 2018 01:26:38 +0900 Subject: [PATCH 09/12] Remove redundant NULL check --- Modules/_abc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index cea4582db28470d..104267e37991baf 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -656,9 +656,6 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, } for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) { PyObject *mro_item = PyTuple_GET_ITEM(mro, pos); - if (mro_item == NULL) { - goto end; - } if ((PyObject *)self == mro_item) { if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) { goto end; From 8bcb74c18e390d253c2721ada1014558a7fe5ca6 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 7 Mar 2018 02:09:38 +0900 Subject: [PATCH 10/12] Revert "Move identifiers to function local" This reverts commit e15fc35df11c27e59140231f52c8a0859c661bf9. --- Modules/_abc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 104267e37991baf..e136280856fda2f 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -12,7 +12,13 @@ module _abc PyDoc_STRVAR(_abc__doc__, "Module contains faster C implementation of abc.ABCMeta"); +_Py_IDENTIFIER(__abstractmethods__); +_Py_IDENTIFIER(__class__); +_Py_IDENTIFIER(__dict__); +_Py_IDENTIFIER(__bases__); _Py_IDENTIFIER(_abc_impl); +_Py_IDENTIFIER(__subclasscheck__); +_Py_IDENTIFIER(__subclasshook__); /* A global counter that is incremented each time a class is registered as a virtual subclass of anything. It forces the @@ -252,7 +258,6 @@ _abc__get_dump(PyObject *module, PyObject *self) static int compute_abstract_methods(PyObject *self) { - _Py_IDENTIFIER(__abstractmethods__); int ret = -1; PyObject *abstracts = PyFrozenSet_New(NULL); if (abstracts == NULL) { @@ -262,7 +267,6 @@ compute_abstract_methods(PyObject *self) PyObject *ns = NULL, *items = NULL, *bases = NULL; // Py_XDECREF()ed on error. /* Stage 1: direct abstract methods. */ - _Py_IDENTIFIER(__dict__); ns = _PyObject_GetAttrId(self, &PyId___dict__); if (!ns) { goto error; @@ -307,7 +311,6 @@ compute_abstract_methods(PyObject *self) } /* Stage 2: inherited abstract methods. */ - _Py_IDENTIFIER(__bases__); bases = _PyObject_GetAttrId(self, &PyId___bases__); if (!bases) { goto error; @@ -476,14 +479,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, PyObject *instance) /*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/ { - _Py_IDENTIFIER(__subclasscheck__); PyObject *subtype, *result = NULL, *subclass = NULL; _abc_data *impl = _get_impl(self); if (impl == NULL) { return NULL; } - _Py_IDENTIFIER(__class__); subclass = _PyObject_GetAttrId(instance, &PyId___class__); if (subclass == NULL) { Py_DECREF(impl); @@ -607,7 +608,6 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, } /* 3. Check the subclass hook. */ - _Py_IDENTIFIER(__subclasshook__); ok = _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId___subclasshook__, subclass, NULL); if (ok == NULL) { From 697abd2094538efa443286d74d84f218212c8cfc Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 7 Mar 2018 02:10:51 +0900 Subject: [PATCH 11/12] Move __mro__ identifier to top of the file --- Modules/_abc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index e136280856fda2f..862883987fb7f8c 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -16,6 +16,7 @@ _Py_IDENTIFIER(__abstractmethods__); _Py_IDENTIFIER(__class__); _Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(__bases__); +_Py_IDENTIFIER(__mro__); _Py_IDENTIFIER(_abc_impl); _Py_IDENTIFIER(__subclasscheck__); _Py_IDENTIFIER(__subclasshook__); @@ -643,7 +644,6 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, * cls._abc_cache.add(subclass) * return True */ - _Py_IDENTIFIER(__mro__); if (_PyObject_LookupAttrId(subclass, &PyId___mro__, &mro) < 0) { goto end; } From fa4af5fbc92c840418c5a572148c9b6c8c0f6d41 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 7 Mar 2018 15:01:20 +0900 Subject: [PATCH 12/12] rename test func --- Lib/test/test_abc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index a65d44d2645a638..6fc3c95e4a645ef 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -392,7 +392,7 @@ class MyInt(int): self.assertIsInstance(42, A) self.assertIsInstance(42, (A,)) - def test_issubclass(self): + def test_issubclass_bad_arguments(self): class A(metaclass=abc_ABCMeta): pass