@@ -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,22 @@ 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 )
78+ # Automatically include cache entry
79+ cls ._abc_cache .add (subclass )
6980 ABCMeta ._abc_invalidation_counter += 1 # Invalidate negative cache
81+ # Recursively register the subclass in all ABC bases,
82+ # to avoid recursive lookups down the class tree.
83+ # >>> class Ancestor1(ABC): pass
84+ # >>> class Ancestor2(Ancestor1): pass
85+ # >>> class Other: pass
86+ # >>> Ancestor2.register(Other) # calls Ancestor1.register(Other)
87+ # >>> issubclass(Other, Ancestor2) is True
88+ # >>> issubclass(Other, Ancestor1) is True # already in registry
89+ for base in cls .__bases__ :
90+ if hasattr (base , "_abc_registry" ):
91+ base .register (subclass )
7092 return subclass
7193
7294 def _dump_registry (cls , file = None ):
@@ -137,11 +159,16 @@ def __subclasscheck__(cls, subclass):
137159 if issubclass (subclass , rcls ):
138160 cls ._abc_cache .add (subclass )
139161 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
162+ # Check if it's a subclass of a subclass (recursive).
163+ # If __subclasses__ contain only ABCs,
164+ # calling issubclass(...) will trigger the same __subclasscheck__
165+ # on *every* element of class inheritance tree.
166+ # Performing that only in resence of `def __subclasses__()` classmethod
167+ if cls ._abc_should_check_subclasses :
168+ for scls in cls .__subclasses__ ():
169+ if issubclass (subclass , scls ):
170+ cls ._abc_cache .add (subclass )
171+ return True
145172 # No dice; update negative cache
146173 cls ._abc_negative_cache .add (subclass )
147174 return False
0 commit comments