123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- # Copyright 2007 Google, Inc. All Rights Reserved.
- # Licensed to PSF under a Contributor Agreement.
- """Abstract Base Classes (ABCs) according to PEP 3119."""
- from _weakrefset import WeakSet
- 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.
- The abstract methods can be called using any of the normal
- 'super' call mechanisms.
- Usage:
- class C(metaclass=ABCMeta):
- @abstractmethod
- def my_abstract_method(self, ...):
- ...
- """
- funcobj.__isabstractmethod__ = True
- return funcobj
- class abstractclassmethod(classmethod):
- """
- A decorator indicating abstract classmethods.
- Similar to abstractmethod.
- Usage:
- class C(metaclass=ABCMeta):
- @abstractclassmethod
- def my_abstract_classmethod(cls, ...):
- ...
- 'abstractclassmethod' is deprecated. Use 'classmethod' with
- 'abstractmethod' instead.
- """
- __isabstractmethod__ = True
- def __init__(self, callable):
- callable.__isabstractmethod__ = True
- super().__init__(callable)
- class abstractstaticmethod(staticmethod):
- """
- A decorator indicating abstract staticmethods.
- Similar to abstractmethod.
- Usage:
- class C(metaclass=ABCMeta):
- @abstractstaticmethod
- def my_abstract_staticmethod(...):
- ...
- 'abstractstaticmethod' is deprecated. Use 'staticmethod' with
- 'abstractmethod' instead.
- """
- __isabstractmethod__ = True
- def __init__(self, callable):
- callable.__isabstractmethod__ = True
- super().__init__(callable)
- class abstractproperty(property):
- """
- A decorator indicating abstract properties.
- 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.
- The abstract properties can be called using any of the normal
- 'super' call mechanisms.
- 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)
- 'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
- instead.
- """
- __isabstractmethod__ = True
- 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.
- # Note: this counter is private. Use `abc.get_cache_token()` for
- # external code.
- _abc_invalidation_counter = 0
- def __new__(mcls, name, bases, namespace):
- cls = super().__new__(mcls, name, bases, namespace)
- # 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)
- cls.__abstractmethods__ = frozenset(abstracts)
- # Set up inheritance registry
- 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):
- """Register a virtual subclass of an ABC.
- Returns the subclass, to allow usage as a class decorator.
- """
- if not isinstance(subclass, type):
- raise TypeError("Can only register classes")
- if issubclass(subclass, cls):
- return subclass # 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.
- if issubclass(cls, subclass):
- # This would create a cycle, which is bad for the algorithm below
- raise RuntimeError("Refusing to create an inheritance cycle")
- cls._abc_registry.add(subclass)
- ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
- return subclass
- def _dump_registry(cls, file=None):
- """Debug helper to print the ABC registry."""
- print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
- print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
- for name in sorted(cls.__dict__.keys()):
- if name.startswith("_abc_"):
- value = getattr(cls, name)
- print("%s: %r" % (name, value), file=file)
- def __instancecheck__(cls, instance):
- """Override for isinstance(instance, cls)."""
- # 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})
- def __subclasscheck__(cls, subclass):
- """Override for issubclass(subclass, cls)."""
- # Check cache
- if subclass in cls._abc_cache:
- return True
- # Check negative cache; may have to invalidate
- if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
- # Invalidate the negative cache
- cls._abc_negative_cache = WeakSet()
- cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
- elif subclass in cls._abc_negative_cache:
- return False
- # Check the subclass hook
- 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
- # Check if it's a direct subclass
- if cls in getattr(subclass, '__mro__', ()):
- cls._abc_cache.add(subclass)
- return True
- # Check if it's a subclass of a registered class (recursive)
- for rcls in cls._abc_registry:
- if issubclass(subclass, rcls):
- cls._abc_cache.add(subclass)
- return True
- # Check if it's a subclass of a subclass (recursive)
- for scls in cls.__subclasses__():
- if issubclass(subclass, scls):
- cls._abc_cache.add(subclass)
- return True
- # No dice; update negative cache
- cls._abc_negative_cache.add(subclass)
- return False
- class ABC(metaclass=ABCMeta):
- """Helper class that provides a standard way to create an ABC using
- inheritance.
- """
- pass
- def get_cache_token():
- """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.
- """
- return ABCMeta._abc_invalidation_counter
|