returns the current abc cache token the token is an opaque object supporting equality testing identifying the current version of the abc cache for virtual subclasses the token changes with every call to register on any abc metaclass for defining abstract base classes abcs use this metaclass to create an abc an abc can be subclassed directly and then acts as a mixin class you can also register unrelated concrete classes even builtin classes and unrelated abcs as virtual subclasses these and their descendants will be considered subclasses of the registering abc by the builtin issubclass function but the registering abc won t show up in their mro method resolution order nor will method implementations defined by the registering abc be callable not even via super a global counter that is incremented each time a class is registered as a virtual subclass of anything it forces the negative cache to be cleared before its next use note this counter is private use abc getcachetoken for external code compute set of abstract method names set up inheritance registry register a virtual subclass of an abc returns the subclass to allow usage as a class decorator subtle test for cycles after testing for already a subclass this means we allow x registerx and interpret it as a noop this would create a cycle which is bad for the algorithm below debug helper to print the abc registry printfclass cls module cls qualname filefile printfinv counter getcachetoken filefile for name in cls dict if name startswithabc value getattrcls name if isinstancevalue weakset value setvalue printfname value r filefile def abcregistryclearcls clear the caches for debugging or testing cls abccache clear cls abcnegativecache clear def instancecheckcls instance inline the cache checking fall back to the subclass check override for issubclasssubclass cls if not isinstancesubclass type raise typeerror issubclass arg 1 must be a class check cache if subclass in cls abccache return true check negative cache may have to invalidate if cls abcnegativecacheversion abcmeta abcinvalidationcounter invalidate the negative cache cls abcnegativecache weakset cls abcnegativecacheversion abcmeta abcinvalidationcounter elif subclass in cls abcnegativecache return false check the subclass hook ok cls subclasshooksubclass if ok is not notimplemented assert isinstanceok bool if ok cls abccache addsubclass else cls abcnegativecache addsubclass return ok check if it s a direct subclass if cls in getattrsubclass mro cls abccache addsubclass return true check if it s a subclass of a registered class recursive for rcls in cls abcregistry if issubclasssubclass rcls cls abccache addsubclass return true check if it s a subclass of a subclass recursive for scls in cls subclasses if issubclasssubclass scls cls abccache addsubclass return true no dice update negative cache cls abcnegativecache addsubclass return false returns the current abc cache token the token is an opaque object supporting equality testing identifying the current version of the abc cache for virtual subclasses the token changes with every call to register on any abc metaclass for defining abstract base classes abcs use this metaclass to create an abc an abc can be subclassed directly and then acts as a mix in class you can also register unrelated concrete classes even built in classes and unrelated abcs as virtual subclasses these and their descendants will be considered subclasses of the registering abc by the built in issubclass function but the registering abc won t show up in their mro method resolution order nor will method implementations defined by the registering abc be callable not even via super a global counter that is incremented each time a class is registered as a virtual subclass of anything it forces the negative cache to be cleared before its next use note this counter is private use abc get_cache_token for external code compute set of abstract method names set up inheritance registry register a virtual subclass of an abc returns the subclass to allow usage as a class decorator already a subclass subtle test for cycles after testing for already a subclass this means we allow x register x and interpret it as a no op this would create a cycle which is bad for the algorithm below invalidate negative cache debug helper to print the abc registry clear the registry for debugging or testing clear the caches for debugging or testing override for isinstance instance cls inline the cache checking fall back to the subclass check override for issubclass subclass cls check cache check negative cache may have to invalidate invalidate the negative cache check the subclass hook check if it s a direct subclass check if it s a subclass of a registered class recursive check if it s a subclass of a subclass recursive no dice update negative cache
from _weakrefset import WeakSet


def get_cache_token():
    
    return ABCMeta._abc_invalidation_counter


class ABCMeta(type):
    

    
    
    
    
    
    _abc_invalidation_counter = 0

    def __new__(mcls, name, bases, namespace, /, **kwargs):
        cls = super().__new__(mcls, name, bases, namespace, **kwargs)
        
        abstracts = {name
                     for name, value in namespace.items()
                     if getattr(value, "__isabstractmethod__", False)}
        for base in bases:
            for name in getattr(base, "__abstractmethods__", set()):
                value = getattr(cls, name, None)
                if getattr(value, "__isabstractmethod__", False):
                    abstracts.add(name)
        cls.__abstractmethods__ = frozenset(abstracts)
        
        cls._abc_registry = WeakSet()
        cls._abc_cache = WeakSet()
        cls._abc_negative_cache = WeakSet()
        cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        return cls

    def register(cls, subclass):
        
        if not isinstance(subclass, type):
            raise TypeError("Can only register classes")
        if issubclass(subclass, cls):
            return subclass  
        
        
        if issubclass(cls, subclass):
            
            raise RuntimeError("Refusing to create an inheritance cycle")
        cls._abc_registry.add(subclass)
        ABCMeta._abc_invalidation_counter += 1  
        return subclass

    def _dump_registry(cls, file=None):
        
        print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
        print(f"Inv. counter: {get_cache_token()}", file=file)
        for name in cls.__dict__:
            if name.startswith("_abc_"):
                value = getattr(cls, name)
                if isinstance(value, WeakSet):
                    value = set(value)
                print(f"{name}: {value!r}", file=file)

    def _abc_registry_clear(cls):
        
        cls._abc_registry.clear()

    def _abc_caches_clear(cls):
        
        cls._abc_cache.clear()
        cls._abc_negative_cache.clear()

    def __instancecheck__(cls, instance):
        
        
        subclass = instance.__class__
        if subclass in cls._abc_cache:
            return True
        subtype = type(instance)
        if subtype is subclass:
            if (cls._abc_negative_cache_version ==
                ABCMeta._abc_invalidation_counter and
                subclass in cls._abc_negative_cache):
                return False
            
            return cls.__subclasscheck__(subclass)
        return any(cls.__subclasscheck__(c) for c in (subclass, subtype))

    def __subclasscheck__(cls, subclass):
        
        if not isinstance(subclass, type):
            raise TypeError('issubclass() arg 1 must be a class')
        
        if subclass in cls._abc_cache:
            return True
        
        if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
            
            cls._abc_negative_cache = WeakSet()
            cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        elif subclass in cls._abc_negative_cache:
            return False
        
        ok = cls.__subclasshook__(subclass)
        if ok is not NotImplemented:
            assert isinstance(ok, bool)
            if ok:
                cls._abc_cache.add(subclass)
            else:
                cls._abc_negative_cache.add(subclass)
            return ok
        
        if cls in getattr(subclass, '__mro__', ()):
            cls._abc_cache.add(subclass)
            return True
        
        for rcls in cls._abc_registry:
            if issubclass(subclass, rcls):
                cls._abc_cache.add(subclass)
                return True
        
        for scls in cls.__subclasses__():
            if issubclass(subclass, scls):
                cls._abc_cache.add(subclass)
                return True
        
        cls._abc_negative_cache.add(subclass)
        return False