diff --git a/Lib/_py_abc.py b/Lib/_py_abc.py index c870ae9048b4f1..291d4bfa756f8d 100644 --- a/Lib/_py_abc.py +++ b/Lib/_py_abc.py @@ -32,8 +32,7 @@ class ABCMeta(type): # external code. _abc_invalidation_counter = 0 - def __new__(mcls, name, bases, namespace, /, **kwargs): - cls = super().__new__(mcls, name, bases, namespace, **kwargs) + def __init__(cls, name, bases, namespace, /, **kwargs): # Compute set of abstract method names abstracts = {name for name, value in namespace.items() @@ -49,7 +48,6 @@ def __new__(mcls, name, bases, namespace, /, **kwargs): cls._abc_cache = WeakSet() cls._abc_negative_cache = WeakSet() cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter - return cls def register(cls, subclass): """Register a virtual subclass of an ABC. @@ -91,6 +89,8 @@ def _abc_caches_clear(cls): def __instancecheck__(cls, instance): """Override for isinstance(instance, cls).""" + if '_abc_cache' not in cls.__dict__: + cls.__class__.__init__(cls, cls.__name__, cls.__bases__, cls.__dict__) # Inline the cache checking subclass = instance.__class__ if subclass in cls._abc_cache: @@ -107,6 +107,8 @@ def __instancecheck__(cls, instance): def __subclasscheck__(cls, subclass): """Override for issubclass(subclass, cls).""" + if '_abc_cache' not in cls.__dict__: + cls.__class__.__init__(cls, cls.__name__, cls.__bases__, cls.__dict__) if not isinstance(subclass, type): raise TypeError('issubclass() arg 1 must be a class') # Check cache diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 5ce57cc209ea85..439cbb56339962 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -682,6 +682,62 @@ class B(A, metaclass=abc_ABCMeta, name="test"): pass self.assertEqual(saved_kwargs, dict(name="test")) + + def test_subclasscheck_in_init_subclass1(self): + # test for gh-82266 + class A(metaclass=abc_ABCMeta): + pass + + class B(metaclass=abc_ABCMeta): + def __init_subclass__(cls): + assert not issubclass(cls, A) + + class C(A): + pass + + try: + class AB(A, B): + pass + except Exception: + pass + + self.assertTrue(issubclass(C, A)) + self.assertFalse(issubclass(C, B)) + + def test_subclasscheck_in_init_subclass2(self): + # test for gh-116093 + class A(metaclass=abc_ABCMeta): + pass + + class B(metaclass=abc_ABCMeta): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__() + issubclass(A, A) + issubclass(A, B) + + class AB(A, B): + pass + + self.assertFalse(issubclass(A, B)) + + def test_subclasscheck_in_init_subclass3(self): + # test for gh-119699 + class A(metaclass=abc_ABCMeta): + pass + + class B(A): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__() + issubclass(B, C) + + class C(A): + pass + + class BC(B, C): + pass + + self.assertTrue(issubclass(B, B)) + return TestLegacyAPI, TestABC, TestABCWithInitSubclass TestLegacyAPI_Py, TestABC_Py, TestABCWithInitSubclass_Py = test_factory(abc.ABCMeta, diff --git a/Misc/NEWS.d/next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst b/Misc/NEWS.d/next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst new file mode 100644 index 00000000000000..73da45a5984b6a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst @@ -0,0 +1,2 @@ +Fixed an issue with :meth:`~class.__subclasscheck__` where it can be called within :meth:`~object.__init_subclass__` before the subclass is initialized with the necessary ABC cache. +Patch by Ben Hsing diff --git a/Modules/_abc.c b/Modules/_abc.c index 4f4b24b035db4a..fc00de68001d55 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -621,6 +621,11 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, if (impl == NULL) { return NULL; } + PyObject *dict = _PyType_GetDict((PyTypeObject *)self); + if (!PyDict_Contains(dict, &_Py_ID(_abc_impl))) { + _abc__abc_init(module, self); + impl = _get_impl(module, self); + } subclass = PyObject_GetAttr(instance, &_Py_ID(__class__)); if (subclass == NULL) { @@ -715,6 +720,11 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, if (impl == NULL) { return NULL; } + PyObject *dict = _PyType_GetDict((PyTypeObject *)self); + if (!PyDict_Contains(dict, &_Py_ID(_abc_impl))) { + _abc__abc_init(module, self); + impl = _get_impl(module, self); + } /* 1. Check cache. */ incache = _in_weak_set(impl, &impl->_abc_cache, subclass); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy