1# Copyright 2007 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""Abstract Base Classes (ABCs) according to PEP 3119."""
5
6from _weakrefset import WeakSet
7
8
9def abstractmethod(funcobj):
10    """A decorator indicating abstract methods.
11
12    Requires that the metaclass is ABCMeta or derived from it.  A
13    class that has a metaclass derived from ABCMeta cannot be
14    instantiated unless all of its abstract methods are overridden.
15    The abstract methods can be called using any of the normal
16    'super' call mechanisms.
17
18    Usage:
19
20        class C(metaclass=ABCMeta):
21            @abstractmethod
22            def my_abstract_method(self, ...):
23                ...
24    """
25    funcobj.__isabstractmethod__ = True
26    return funcobj
27
28
29class abstractclassmethod(classmethod):
30    """
31    A decorator indicating abstract classmethods.
32
33    Similar to abstractmethod.
34
35    Usage:
36
37        class C(metaclass=ABCMeta):
38            @abstractclassmethod
39            def my_abstract_classmethod(cls, ...):
40                ...
41
42    'abstractclassmethod' is deprecated. Use 'classmethod' with
43    'abstractmethod' instead.
44    """
45
46    __isabstractmethod__ = True
47
48    def __init__(self, callable):
49        callable.__isabstractmethod__ = True
50        super().__init__(callable)
51
52
53class abstractstaticmethod(staticmethod):
54    """
55    A decorator indicating abstract staticmethods.
56
57    Similar to abstractmethod.
58
59    Usage:
60
61        class C(metaclass=ABCMeta):
62            @abstractstaticmethod
63            def my_abstract_staticmethod(...):
64                ...
65
66    'abstractstaticmethod' is deprecated. Use 'staticmethod' with
67    'abstractmethod' instead.
68    """
69
70    __isabstractmethod__ = True
71
72    def __init__(self, callable):
73        callable.__isabstractmethod__ = True
74        super().__init__(callable)
75
76
77class abstractproperty(property):
78    """
79    A decorator indicating abstract properties.
80
81    Requires that the metaclass is ABCMeta or derived from it.  A
82    class that has a metaclass derived from ABCMeta cannot be
83    instantiated unless all of its abstract properties are overridden.
84    The abstract properties can be called using any of the normal
85    'super' call mechanisms.
86
87    Usage:
88
89        class C(metaclass=ABCMeta):
90            @abstractproperty
91            def my_abstract_property(self):
92                ...
93
94    This defines a read-only property; you can also define a read-write
95    abstract property using the 'long' form of property declaration:
96
97        class C(metaclass=ABCMeta):
98            def getx(self): ...
99            def setx(self, value): ...
100            x = abstractproperty(getx, setx)
101
102    'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
103    instead.
104    """
105
106    __isabstractmethod__ = True
107
108
109class ABCMeta(type):
110
111    """Metaclass for defining Abstract Base Classes (ABCs).
112
113    Use this metaclass to create an ABC.  An ABC can be subclassed
114    directly, and then acts as a mix-in class.  You can also register
115    unrelated concrete classes (even built-in classes) and unrelated
116    ABCs as 'virtual subclasses' -- these and their descendants will
117    be considered subclasses of the registering ABC by the built-in
118    issubclass() function, but the registering ABC won't show up in
119    their MRO (Method Resolution Order) nor will method
120    implementations defined by the registering ABC be callable (not
121    even via super()).
122
123    """
124
125    # A global counter that is incremented each time a class is
126    # registered as a virtual subclass of anything.  It forces the
127    # negative cache to be cleared before its next use.
128    # Note: this counter is private. Use `abc.get_cache_token()` for
129    #       external code.
130    _abc_invalidation_counter = 0
131
132    def __new__(mcls, name, bases, namespace):
133        cls = super().__new__(mcls, name, bases, namespace)
134        # Compute set of abstract method names
135        abstracts = {name
136                     for name, value in namespace.items()
137                     if getattr(value, "__isabstractmethod__", False)}
138        for base in bases:
139            for name in getattr(base, "__abstractmethods__", set()):
140                value = getattr(cls, name, None)
141                if getattr(value, "__isabstractmethod__", False):
142                    abstracts.add(name)
143        cls.__abstractmethods__ = frozenset(abstracts)
144        # Set up inheritance registry
145        cls._abc_registry = WeakSet()
146        cls._abc_cache = WeakSet()
147        cls._abc_negative_cache = WeakSet()
148        cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
149        return cls
150
151    def register(cls, subclass):
152        """Register a virtual subclass of an ABC.
153
154        Returns the subclass, to allow usage as a class decorator.
155        """
156        if not isinstance(subclass, type):
157            raise TypeError("Can only register classes")
158        if issubclass(subclass, cls):
159            return subclass  # Already a subclass
160        # Subtle: test for cycles *after* testing for "already a subclass";
161        # this means we allow X.register(X) and interpret it as a no-op.
162        if issubclass(cls, subclass):
163            # This would create a cycle, which is bad for the algorithm below
164            raise RuntimeError("Refusing to create an inheritance cycle")
165        cls._abc_registry.add(subclass)
166        ABCMeta._abc_invalidation_counter += 1  # Invalidate negative cache
167        return subclass
168
169    def _dump_registry(cls, file=None):
170        """Debug helper to print the ABC registry."""
171        print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
172        print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
173        for name in sorted(cls.__dict__.keys()):
174            if name.startswith("_abc_"):
175                value = getattr(cls, name)
176                print("%s: %r" % (name, value), file=file)
177
178    def __instancecheck__(cls, instance):
179        """Override for isinstance(instance, cls)."""
180        # Inline the cache checking
181        subclass = instance.__class__
182        if subclass in cls._abc_cache:
183            return True
184        subtype = type(instance)
185        if subtype is subclass:
186            if (cls._abc_negative_cache_version ==
187                ABCMeta._abc_invalidation_counter and
188                subclass in cls._abc_negative_cache):
189                return False
190            # Fall back to the subclass check.
191            return cls.__subclasscheck__(subclass)
192        return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
193
194    def __subclasscheck__(cls, subclass):
195        """Override for issubclass(subclass, cls)."""
196        # Check cache
197        if subclass in cls._abc_cache:
198            return True
199        # Check negative cache; may have to invalidate
200        if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
201            # Invalidate the negative cache
202            cls._abc_negative_cache = WeakSet()
203            cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
204        elif subclass in cls._abc_negative_cache:
205            return False
206        # Check the subclass hook
207        ok = cls.__subclasshook__(subclass)
208        if ok is not NotImplemented:
209            assert isinstance(ok, bool)
210            if ok:
211                cls._abc_cache.add(subclass)
212            else:
213                cls._abc_negative_cache.add(subclass)
214            return ok
215        # Check if it's a direct subclass
216        if cls in getattr(subclass, '__mro__', ()):
217            cls._abc_cache.add(subclass)
218            return True
219        # Check if it's a subclass of a registered class (recursive)
220        for rcls in cls._abc_registry:
221            if issubclass(subclass, rcls):
222                cls._abc_cache.add(subclass)
223                return True
224        # Check if it's a subclass of a subclass (recursive)
225        for scls in cls.__subclasses__():
226            if issubclass(subclass, scls):
227                cls._abc_cache.add(subclass)
228                return True
229        # No dice; update negative cache
230        cls._abc_negative_cache.add(subclass)
231        return False
232
233
234class ABC(metaclass=ABCMeta):
235    """Helper class that provides a standard way to create an ABC using
236    inheritance.
237    """
238    pass
239
240
241def get_cache_token():
242    """Returns the current ABC cache token.
243
244    The token is an opaque object (supporting equality testing) identifying the
245    current version of the ABC cache for virtual subclasses. The token changes
246    with every call to ``register()`` on any ABC.
247    """
248    return ABCMeta._abc_invalidation_counter
249