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
30 changes: 29 additions & 1 deletion Lib/test/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
"""Unit tests for abc.py."""

import unittest
import weakref

import abc
import _py_abc
from inspect import isabstract
from test.support import warnings_helper
from test.support import gc_collect, warnings_helper

def test_factory(abc_ABCMeta, abc_get_cache_token):
class TestLegacyAPI(unittest.TestCase):
Expand Down Expand Up @@ -380,6 +381,33 @@ class C(str): pass
self.assertIsSubclass(C, A)
self.assertIsSubclass(C, (A,))

def test_instancecheck_class_changes_in_subclasscheck(self):
class Original:
def __getattribute__(self, name):
if name == '__class__':
return int
return object.__getattribute__(self, name)

class Replacement:
pass

instance = Original()
original = weakref.ref(Original)
del Original

class Meta(abc_ABCMeta):
def __subclasscheck__(cls, candidate):
if candidate is int:
instance.__class__ = Replacement
gc_collect()
self.assertIsNotNone(original())
return False

class Interface(metaclass=Meta):
pass

self.assertNotIsInstance(instance, Interface)

def test_instancecheck_no_class(self):
# gh-153772: __instancecheck__ must fall back to type(instance)
# when the instance has no __class__, matching isinstance().
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a possible crash in :class:`abc.ABCMeta` instance checks when a subclass-checking callback changes the instance’s class.
4 changes: 4 additions & 0 deletions Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,9 +664,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
subclass);
goto end;
}
/* A subclass check can change the instance's actual type. */
Py_INCREF(subtype);
result = PyObject_CallMethodOneArg(self, &_Py_ID(__subclasscheck__),
subclass);
if (result == NULL) {
Py_DECREF(subtype);
goto end;
}

Expand All @@ -684,6 +687,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
default:
Py_UNREACHABLE();
}
Py_DECREF(subtype);

end:
Py_XDECREF(impl);
Expand Down
Loading