abc.py 7.87 KB
Newer Older
1 2 3 4 5
# Copyright 2007 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

"""Abstract Base Classes (ABCs) according to PEP 3119."""

6
from _weakrefset import WeakSet
7 8 9 10 11 12 13

def abstractmethod(funcobj):
    """A decorator indicating abstract methods.

    Requires that the metaclass is ABCMeta or derived from it.  A
    class that has a metaclass derived from ABCMeta cannot be
    instantiated unless all of its abstract methods are overridden.
14
    The abstract methods can be called using any of the normal
15 16 17 18 19 20 21 22 23 24 25 26 27
    'super' call mechanisms.

    Usage:

        class C(metaclass=ABCMeta):
            @abstractmethod
            def my_abstract_method(self, ...):
                ...
    """
    funcobj.__isabstractmethod__ = True
    return funcobj


28
class abstractclassmethod(classmethod):
29 30
    """
    A decorator indicating abstract classmethods.
31 32 33 34 35 36 37 38 39

    Similar to abstractmethod.

    Usage:

        class C(metaclass=ABCMeta):
            @abstractclassmethod
            def my_abstract_classmethod(cls, ...):
                ...
40 41 42

    'abstractclassmethod' is deprecated. Use 'classmethod' with
    'abstractmethod' instead.
43 44 45 46 47 48 49 50 51 52
    """

    __isabstractmethod__ = True

    def __init__(self, callable):
        callable.__isabstractmethod__ = True
        super().__init__(callable)


class abstractstaticmethod(staticmethod):
53 54
    """
    A decorator indicating abstract staticmethods.
55 56 57 58 59 60 61 62 63

    Similar to abstractmethod.

    Usage:

        class C(metaclass=ABCMeta):
            @abstractstaticmethod
            def my_abstract_staticmethod(...):
                ...
64 65 66

    'abstractstaticmethod' is deprecated. Use 'staticmethod' with
    'abstractmethod' instead.
67 68 69 70 71 72 73 74 75
    """

    __isabstractmethod__ = True

    def __init__(self, callable):
        callable.__isabstractmethod__ = True
        super().__init__(callable)


76
class abstractproperty(property):
77 78
    """
    A decorator indicating abstract properties.
79 80 81 82

    Requires that the metaclass is ABCMeta or derived from it.  A
    class that has a metaclass derived from ABCMeta cannot be
    instantiated unless all of its abstract properties are overridden.
83
    The abstract properties can be called using any of the normal
84
    'super' call mechanisms.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

    Usage:

        class C(metaclass=ABCMeta):
            @abstractproperty
            def my_abstract_property(self):
                ...

    This defines a read-only property; you can also define a read-write
    abstract property using the 'long' form of property declaration:

        class C(metaclass=ABCMeta):
            def getx(self): ...
            def setx(self, value): ...
            x = abstractproperty(getx, setx)
100 101 102

    'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
    instead.
103
    """
104

105 106 107
    __isabstractmethod__ = True


108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
class ABCMeta(type):

    """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.
127
    _abc_invalidation_counter = 0
128 129

    def __new__(mcls, name, bases, namespace):
130
        cls = super().__new__(mcls, name, bases, namespace)
131 132 133 134 135 136 137 138 139
        # Compute set of abstract method names
        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)
Christian Heimes's avatar
Christian Heimes committed
140
        cls.__abstractmethods__ = frozenset(abstracts)
141
        # Set up inheritance registry
142 143 144
        cls._abc_registry = WeakSet()
        cls._abc_cache = WeakSet()
        cls._abc_negative_cache = WeakSet()
145
        cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
146 147
        return cls

148
    def register(cls, subclass):
149 150 151 152
        """Register a virtual subclass of an ABC.

        Returns the subclass, to allow usage as a class decorator.
        """
153
        if not isinstance(subclass, type):
154 155
            raise TypeError("Can only register classes")
        if issubclass(subclass, cls):
156
            return subclass  # Already a subclass
157 158 159 160 161
        # Subtle: test for cycles *after* testing for "already a subclass";
        # this means we allow X.register(X) and interpret it as a no-op.
        if issubclass(cls, subclass):
            # This would create a cycle, which is bad for the algorithm below
            raise RuntimeError("Refusing to create an inheritance cycle")
162 163
        cls._abc_registry.add(subclass)
        ABCMeta._abc_invalidation_counter += 1  # Invalidate negative cache
164
        return subclass
165 166 167 168

    def _dump_registry(cls, file=None):
        """Debug helper to print the ABC registry."""
        print("Class: %s.%s" % (cls.__module__, cls.__name__), file=file)
169
        print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
170
        for name in sorted(cls.__dict__.keys()):
171
            if name.startswith("_abc_"):
172 173 174 175 176
                value = getattr(cls, name)
                print("%s: %r" % (name, value), file=file)

    def __instancecheck__(cls, instance):
        """Override for isinstance(instance, cls)."""
Christian Heimes's avatar
Christian Heimes committed
177 178 179 180 181 182 183 184 185 186 187 188 189
        # Inline the cache checking
        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
            # Fall back to the subclass check.
            return cls.__subclasscheck__(subclass)
        return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
190 191 192 193

    def __subclasscheck__(cls, subclass):
        """Override for issubclass(subclass, cls)."""
        # Check cache
194
        if subclass in cls._abc_cache:
195 196
            return True
        # Check negative cache; may have to invalidate
197
        if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
198
            # Invalidate the negative cache
199
            cls._abc_negative_cache = WeakSet()
200 201
            cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        elif subclass in cls._abc_negative_cache:
202 203 204 205 206 207
            return False
        # Check the subclass hook
        ok = cls.__subclasshook__(subclass)
        if ok is not NotImplemented:
            assert isinstance(ok, bool)
            if ok:
208
                cls._abc_cache.add(subclass)
209
            else:
210
                cls._abc_negative_cache.add(subclass)
211 212
            return ok
        # Check if it's a direct subclass
213
        if cls in getattr(subclass, '__mro__', ()):
214
            cls._abc_cache.add(subclass)
215 216
            return True
        # Check if it's a subclass of a registered class (recursive)
217
        for rcls in cls._abc_registry:
218
            if issubclass(subclass, rcls):
219
                cls._abc_cache.add(subclass)
220 221 222 223
                return True
        # Check if it's a subclass of a subclass (recursive)
        for scls in cls.__subclasses__():
            if issubclass(subclass, scls):
224
                cls._abc_cache.add(subclass)
225 226
                return True
        # No dice; update negative cache
227
        cls._abc_negative_cache.add(subclass)
228
        return False