Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Lib/test/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,24 @@ class MyInt(int):
self.assertIsInstance(42, A)
self.assertIsInstance(42, (A,))

def test_issubclass_bad_arguments(self):
class A(metaclass=abc_ABCMeta):
pass

with self.assertRaises(TypeError):
issubclass({}, A) # unhashable

with self.assertRaises(TypeError):
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):
issubclass(C(), A)

def test_all_new_methods_are_called(self):
class A(metaclass=abc_ABCMeta):
pass
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix C implemetation of ``ABC.__subclasscheck__(cls, subclass)`` crashed when
``subclass`` is not a type object.
37 changes: 25 additions & 12 deletions Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
Expand Down Expand Up @@ -568,7 +569,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);
Expand Down Expand Up @@ -637,20 +638,31 @@ _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) {
/* 4. Check if it's a direct subclass.
*
* if cls in getattr(subclass, '__mro__', ()):
* cls._abc_cache.add(subclass)
* return True
*/
if (_PyObject_LookupAttrId(subclass, &PyId___mro__, &mro) < 0) {
goto end;
}
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;
}
if ((PyObject *)self == mro_item) {
if (_add_to_weak_set(&impl->_abc_cache, subclass) < 0) {
for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) {
PyObject *mro_item = PyTuple_GET_ITEM(mro, pos);
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;
}
}

Expand Down Expand Up @@ -690,7 +702,8 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
result = Py_False;

end:
Py_XDECREF(impl);
Py_DECREF(impl);
Py_XDECREF(mro);
Py_XDECREF(subclasses);
Py_XINCREF(result);
return result;
Expand Down