@@ -578,6 +578,8 @@ _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass)
578578 if (result < 0 ) {
579579 return NULL ;
580580 }
581+
582+ /* Actual registration */
581583 _abc_data * impl = _get_impl (module , self );
582584 if (impl == NULL ) {
583585 return NULL ;
@@ -588,6 +590,49 @@ _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass)
588590 }
589591 Py_DECREF (impl );
590592
593+ /* Recursively register the subclass in all ABC bases, to avoid recursive lookups.
594+ >>> class Ancestor1(ABC): pass
595+ >>> class Ancestor2(Ancestor1): pass
596+ >>> class Other: pass
597+ >>> Ancestor2.register(Other) # same result for Ancestor1.register(Other)
598+ >>> issubclass(Other, Ancestor2) is True
599+ >>> issubclass(Other, Ancestor1) is True
600+ */
601+ PyObject * mro = PyObject_GetAttrString (self , "__mro__" );
602+ if (mro == NULL ) {
603+ return NULL ;
604+ }
605+
606+ if (!PyTuple_Check (mro )) {
607+ PyErr_SetString (PyExc_TypeError , "__mro__ is not tuple" );
608+ goto error ;
609+ }
610+
611+ for (Py_ssize_t pos = 0 ; pos < PyTuple_GET_SIZE (mro ); pos ++ ) {
612+ PyObject * base_class = PyTuple_GET_ITEM (mro , pos ); // borrowed
613+ PyObject * base_class_data ;
614+
615+ if (PyObject_GetOptionalAttr (base_class , & _Py_ID (_abc_impl ),
616+ & base_class_data ) < 0 ) {
617+ goto error ;
618+ }
619+
620+ if (PyErr_Occurred ()) {
621+ goto error ;
622+ }
623+
624+ if (base_class_data == NULL ) {
625+ // not ABC class
626+ continue ;
627+ }
628+
629+ _abc_data * base_class_state = _abc_data_CAST (base_class_data );
630+ if (_add_to_weak_set (base_class_state , & base_class_state -> _abc_registry , subclass ) < 0 ) {
631+ Py_DECREF (base_class_data );
632+ goto error ;
633+ }
634+ }
635+
591636 /* Invalidate negative cache */
592637 increment_invalidation_counter (get_abc_state (module ));
593638
@@ -602,6 +647,10 @@ _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass)
602647 }
603648 }
604649 return Py_NewRef (subclass );
650+
651+ error :
652+ Py_XDECREF (mro );
653+ return NULL ;
605654}
606655
607656
@@ -710,6 +759,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
710759 PyErr_SetString (PyExc_TypeError , "issubclass() arg 1 must be a class" );
711760 return NULL ;
712761 }
762+ PyTypeObject * cls = (PyTypeObject * )self ;
713763
714764 PyObject * ok , * subclasses = NULL , * result = NULL ;
715765 _abcmodule_state * state = NULL ;
@@ -800,32 +850,43 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
800850 goto end ;
801851 }
802852
803- /* 6. Check if it's a subclass of a subclass (recursive). */
804- subclasses = PyObject_CallMethod (self , "__subclasses__" , NULL );
805- if (subclasses == NULL ) {
806- goto end ;
807- }
808- if (!PyList_Check (subclasses )) {
809- PyErr_SetString (PyExc_TypeError , "__subclasses__() must return a list" );
810- goto end ;
811- }
812- for (pos = 0 ; pos < PyList_GET_SIZE (subclasses ); pos ++ ) {
813- PyObject * scls = PyList_GetItemRef (subclasses , pos );
814- if (scls == NULL ) {
815- goto end ;
816- }
817- int r = PyObject_IsSubclass (subclass , scls );
818- Py_DECREF (scls );
819- if (r > 0 ) {
820- if (_add_to_weak_set (impl , & impl -> _abc_cache , subclass ) < 0 ) {
821- goto end ;
822- }
823- result = Py_True ;
824- goto end ;
825- }
826- if (r < 0 ) {
827- goto end ;
828- }
853+ /* 6. Check if it's a subclass of a subclass (recursive).
854+ >>> class Ancestor: __subclasses__ = lambda: [Other]
855+ >>> class Other: pass
856+ >>> isinstance(Other, Ancestor) is True
857+
858+ Do not iterate over cls.__subclasses__() because it returns the entire class tree,
859+ not just direct children, which leads to O(n^2) lookup.
860+ */
861+ PyObject * dict = _PyType_GetDict (cls ); // borrowed
862+ PyObject * subclasses_own_method = PyDict_GetItemString (dict , "__subclasses__" ); // borrowed
863+ if (subclasses_own_method ) {
864+ subclasses = PyObject_CallNoArgs (subclasses_own_method );
865+ if (subclasses == NULL ) {
866+ goto end ;
867+ }
868+ if (!PyList_Check (subclasses )) {
869+ PyErr_SetString (PyExc_TypeError , "__subclasses__() must return a list" );
870+ goto end ;
871+ }
872+ for (pos = 0 ; pos < PyList_GET_SIZE (subclasses ); pos ++ ) {
873+ PyObject * scls = PyList_GetItemRef (subclasses , pos );
874+ if (scls == NULL ) {
875+ goto end ;
876+ }
877+ int r = PyObject_IsSubclass (subclass , scls );
878+ Py_DECREF (scls );
879+ if (r > 0 ) {
880+ if (_add_to_weak_set (impl , & impl -> _abc_cache , subclass ) < 0 ) {
881+ goto end ;
882+ }
883+ result = Py_True ;
884+ goto end ;
885+ }
886+ if (r < 0 ) {
887+ goto end ;
888+ }
889+ }
829890 }
830891
831892 /* No dice; update negative cache. */
0 commit comments