@@ -49,6 +49,14 @@ def __new__(mcls, name, bases, namespace, /, **kwargs):
4949 cls ._abc_cache = WeakSet ()
5050 cls ._abc_negative_cache = WeakSet ()
5151 cls ._abc_negative_cache_version = ABCMeta ._abc_invalidation_counter
52+
53+ # Performance optimization for common case
54+ cls ._abc_should_check_subclasses = False
55+ if "__subclasses__" in namespace :
56+ cls ._abc_should_check_subclasses = True
57+ for base in bases :
58+ if hasattr (base , "_abc_should_check_subclasses" ):
59+ base ._abc_should_check_subclasses = True
5260 return cls
5361
5462 def register (cls , subclass ):
@@ -65,8 +73,20 @@ def register(cls, subclass):
6573 if issubclass (cls , subclass ):
6674 # This would create a cycle, which is bad for the algorithm below
6775 raise RuntimeError ("Refusing to create an inheritance cycle" )
76+ # Add registry entry
6877 cls ._abc_registry .add (subclass )
6978 ABCMeta ._abc_invalidation_counter += 1 # Invalidate negative cache
79+ # Recursively register the subclass in all ABC bases,
80+ # to avoid recursive lookups down the class tree.
81+ # >>> class Ancestor1(ABC): pass
82+ # >>> class Ancestor2(Ancestor1): pass
83+ # >>> class Other: pass
84+ # >>> Ancestor2.register(Other) # calls Ancestor1.register(Other)
85+ # >>> issubclass(Other, Ancestor2) is True
86+ # >>> issubclass(Other, Ancestor1) is True # already in registry
87+ for base in cls .__bases__ :
88+ if hasattr (base , "_abc_registry" ):
89+ base .register (subclass )
7090 return subclass
7191
7292 def _dump_registry (cls , file = None ):
@@ -132,16 +152,25 @@ def __subclasscheck__(cls, subclass):
132152 if cls in getattr (subclass , '__mro__' , ()):
133153 cls ._abc_cache .add (subclass )
134154 return True
155+ # Fast path: check subclass is in weakset directly.
156+ if subclass in cls ._abc_registry :
157+ cls ._abc_cache .add (subclass )
158+ return True
135159 # Check if it's a subclass of a registered class (recursive)
136160 for rcls in cls ._abc_registry :
137161 if issubclass (subclass , rcls ):
138162 cls ._abc_cache .add (subclass )
139163 return True
140- # Check if it's a subclass of a subclass (recursive)
141- for scls in cls .__subclasses__ ():
142- if issubclass (subclass , scls ):
143- cls ._abc_cache .add (subclass )
144- return True
164+ # Check if it's a subclass of a subclass (recursive).
165+ # If __subclasses__ contain only ABCs,
166+ # calling issubclass(...) will trigger the same __subclasscheck__
167+ # on *every* element of class inheritance tree.
168+ # Performing that only in resence of `def __subclasses__()` classmethod
169+ if cls ._abc_should_check_subclasses :
170+ for scls in cls .__subclasses__ ():
171+ if issubclass (subclass , scls ):
172+ cls ._abc_cache .add (subclass )
173+ return True
145174 # No dice; update negative cache
146175 cls ._abc_negative_cache .add (subclass )
147176 return False
0 commit comments