Skip to content

Commit 7b6a0dc

Browse files
committed
gh-92810: Improve nested subclass check performance
1 parent 858d4c0 commit 7b6a0dc

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

Lib/_py_abc.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ def __subclasscheck__(cls, subclass):
140140
return True
141141
# Check if it's a subclass of a subclass (recursive)
142142
for scls in cls.__subclasses__():
143+
if subclass is scls:
144+
# Fast path
145+
if not cls._abc_issubclasscheck_recursive:
146+
cls._abc_cache.add(subclass)
147+
return True
143148
# If inside recursive issubclass check, avoid adding classes
144149
# to any cache because this may drastically increase memory usage.
145150
# Unfortunately, issubclass/__subclasscheck__ don't accept third
@@ -149,6 +154,7 @@ def __subclasscheck__(cls, subclass):
149154
if scls_is_abc:
150155
scls._abc_issubclasscheck_recursive = True
151156
try:
157+
# Perform recursive check
152158
result = issubclass(subclass, scls)
153159
finally:
154160
if scls_is_abc:

Modules/_abc.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,17 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
848848
goto end;
849849
}
850850

851+
if (scls == subclass) {
852+
// Fast path
853+
if (!is_issubclasscheck_recursive(impl)) {
854+
if (_add_to_weak_set(impl, &impl->_abc_cache, subclass) < 0) {
855+
goto end;
856+
}
857+
}
858+
result = Py_True;
859+
goto end;
860+
}
861+
851862
_abc_data *scls_impl;
852863
int scls_is_abc = _get_optional_impl(state, scls, &scls_impl);
853864
if (scls_is_abc < 0) {
@@ -865,6 +876,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
865876
set_issubclasscheck_recursive(scls_impl);
866877
}
867878

879+
// Perform recursive check
868880
int r = PyObject_IsSubclass(subclass, scls);
869881
Py_DECREF(scls);
870882

0 commit comments

Comments
 (0)