From 0780d1c73bc1ab13c145c92ad85a892fc606eaf3 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Wed, 29 May 2024 10:18:53 +0000 Subject: [PATCH 1/5] gh-116093: ABCMeta.__subclasscheck__ now initializes cls if it is not already initialized --- Lib/_py_abc.py | 8 ++++--- Lib/test/test_abc.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ Modules/_abc.c | 10 ++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) 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/Modules/_abc.c b/Modules/_abc.c index 4f4b24b035db4a..4476655205ca1e 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(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(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); From 251c97919b89c6600a519827c7283aa627f81282 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 10:33:04 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst 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..ef81b62d8b8f3e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst @@ -0,0 +1 @@ +Fixed an issue with `ABCMeta.__subclasscheck__` where it can be called within `__init_subclass__` before the subclass is initialized with the necessary ABC cache. From 1d7fd81f72050376b38ebda017297fc9e354d50c Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Wed, 29 May 2024 10:38:13 +0000 Subject: [PATCH 3/5] fixed blurb --- .../next/Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index ef81b62d8b8f3e..21d38c54315c35 100644 --- 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 @@ -1 +1 @@ -Fixed an issue with `ABCMeta.__subclasscheck__` where it can be called within `__init_subclass__` before the subclass is initialized with the necessary ABC cache. +Fixed an issue with ``ABCMeta.__subclasscheck__`` where it can be called within ``__init_subclass__`` before the subclass is initialized with the necessary ABC cache. From 32c9ad5ef17d287a163076046b1003b4ce1dd8a0 Mon Sep 17 00:00:00 2001 From: blhsing Date: Wed, 29 May 2024 19:53:25 +0800 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Kirill Podoprigora --- Modules/_abc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 4476655205ca1e..fc00de68001d55 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -621,7 +621,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, if (impl == NULL) { return NULL; } - PyObject *dict = _PyType_GetDict(self); + PyObject *dict = _PyType_GetDict((PyTypeObject *)self); if (!PyDict_Contains(dict, &_Py_ID(_abc_impl))) { _abc__abc_init(module, self); impl = _get_impl(module, self); @@ -720,7 +720,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, if (impl == NULL) { return NULL; } - PyObject *dict = _PyType_GetDict(self); + PyObject *dict = _PyType_GetDict((PyTypeObject *)self); if (!PyDict_Contains(dict, &_Py_ID(_abc_impl))) { _abc__abc_init(module, self); impl = _get_impl(module, self); From d65db4227a854c6cc9a37e6852356fd2a0ed1d6d Mon Sep 17 00:00:00 2001 From: blhsing Date: Wed, 18 Sep 2024 09:32:37 +0800 Subject: [PATCH 5/5] Update 2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst Made references to methods proper links in news. --- .../Library/2024-05-29-10-32-59.gh-issue-116093.Gcb6Md.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 21d38c54315c35..73da45a5984b6a 100644 --- 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 @@ -1 +1,2 @@ -Fixed an issue with ``ABCMeta.__subclasscheck__`` where it can be called within ``__init_subclass__`` before the subclass is initialized with the necessary ABC cache. +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