+++ /dev/null
-# Copyright 2007 Google, Inc. All Rights Reserved.\r
-# Licensed to PSF under a Contributor Agreement.\r
-\r
-"""Abstract Base Classes (ABCs) according to PEP 3119."""\r
-\r
-import types\r
-\r
-from _weakrefset import WeakSet\r
-\r
-# Instance of old-style class\r
-class _C: pass\r
-_InstanceType = type(_C())\r
-\r
-\r
-def abstractmethod(funcobj):\r
- """A decorator indicating abstract methods.\r
-\r
- Requires that the metaclass is ABCMeta or derived from it. A\r
- class that has a metaclass derived from ABCMeta cannot be\r
- instantiated unless all of its abstract methods are overridden.\r
- The abstract methods can be called using any of the normal\r
- 'super' call mechanisms.\r
-\r
- Usage:\r
-\r
- class C:\r
- __metaclass__ = ABCMeta\r
- @abstractmethod\r
- def my_abstract_method(self, ...):\r
- ...\r
- """\r
- funcobj.__isabstractmethod__ = True\r
- return funcobj\r
-\r
-\r
-class abstractproperty(property):\r
- """A decorator indicating abstract properties.\r
-\r
- Requires that the metaclass is ABCMeta or derived from it. A\r
- class that has a metaclass derived from ABCMeta cannot be\r
- instantiated unless all of its abstract properties are overridden.\r
- The abstract properties can be called using any of the normal\r
- 'super' call mechanisms.\r
-\r
- Usage:\r
-\r
- class C:\r
- __metaclass__ = ABCMeta\r
- @abstractproperty\r
- def my_abstract_property(self):\r
- ...\r
-\r
- This defines a read-only property; you can also define a read-write\r
- abstract property using the 'long' form of property declaration:\r
-\r
- class C:\r
- __metaclass__ = ABCMeta\r
- def getx(self): ...\r
- def setx(self, value): ...\r
- x = abstractproperty(getx, setx)\r
- """\r
- __isabstractmethod__ = True\r
-\r
-\r
-class ABCMeta(type):\r
-\r
- """Metaclass for defining Abstract Base Classes (ABCs).\r
-\r
- Use this metaclass to create an ABC. An ABC can be subclassed\r
- directly, and then acts as a mix-in class. You can also register\r
- unrelated concrete classes (even built-in classes) and unrelated\r
- ABCs as 'virtual subclasses' -- these and their descendants will\r
- be considered subclasses of the registering ABC by the built-in\r
- issubclass() function, but the registering ABC won't show up in\r
- their MRO (Method Resolution Order) nor will method\r
- implementations defined by the registering ABC be callable (not\r
- even via super()).\r
-\r
- """\r
-\r
- # A global counter that is incremented each time a class is\r
- # registered as a virtual subclass of anything. It forces the\r
- # negative cache to be cleared before its next use.\r
- _abc_invalidation_counter = 0\r
-\r
- def __new__(mcls, name, bases, namespace):\r
- cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)\r
- # Compute set of abstract method names\r
- abstracts = set(name\r
- for name, value in namespace.items()\r
- if getattr(value, "__isabstractmethod__", False))\r
- for base in bases:\r
- for name in getattr(base, "__abstractmethods__", set()):\r
- value = getattr(cls, name, None)\r
- if getattr(value, "__isabstractmethod__", False):\r
- abstracts.add(name)\r
- cls.__abstractmethods__ = frozenset(abstracts)\r
- # Set up inheritance registry\r
- cls._abc_registry = WeakSet()\r
- cls._abc_cache = WeakSet()\r
- cls._abc_negative_cache = WeakSet()\r
- cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter\r
- return cls\r
-\r
- def register(cls, subclass):\r
- """Register a virtual subclass of an ABC."""\r
- if not isinstance(subclass, (type, types.ClassType)):\r
- raise TypeError("Can only register classes")\r
- if issubclass(subclass, cls):\r
- return # Already a subclass\r
- # Subtle: test for cycles *after* testing for "already a subclass";\r
- # this means we allow X.register(X) and interpret it as a no-op.\r
- if issubclass(cls, subclass):\r
- # This would create a cycle, which is bad for the algorithm below\r
- raise RuntimeError("Refusing to create an inheritance cycle")\r
- cls._abc_registry.add(subclass)\r
- ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache\r
-\r
- def _dump_registry(cls, file=None):\r
- """Debug helper to print the ABC registry."""\r
- print >> file, "Class: %s.%s" % (cls.__module__, cls.__name__)\r
- print >> file, "Inv.counter: %s" % ABCMeta._abc_invalidation_counter\r
- for name in sorted(cls.__dict__.keys()):\r
- if name.startswith("_abc_"):\r
- value = getattr(cls, name)\r
- print >> file, "%s: %r" % (name, value)\r
-\r
- def __instancecheck__(cls, instance):\r
- """Override for isinstance(instance, cls)."""\r
- # Inline the cache checking when it's simple.\r
- subclass = getattr(instance, '__class__', None)\r
- if subclass is not None and subclass in cls._abc_cache:\r
- return True\r
- subtype = type(instance)\r
- # Old-style instances\r
- if subtype is _InstanceType:\r
- subtype = subclass\r
- if subtype is subclass or subclass is None:\r
- if (cls._abc_negative_cache_version ==\r
- ABCMeta._abc_invalidation_counter and\r
- subtype in cls._abc_negative_cache):\r
- return False\r
- # Fall back to the subclass check.\r
- return cls.__subclasscheck__(subtype)\r
- return (cls.__subclasscheck__(subclass) or\r
- cls.__subclasscheck__(subtype))\r
-\r
- def __subclasscheck__(cls, subclass):\r
- """Override for issubclass(subclass, cls)."""\r
- # Check cache\r
- if subclass in cls._abc_cache:\r
- return True\r
- # Check negative cache; may have to invalidate\r
- if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:\r
- # Invalidate the negative cache\r
- cls._abc_negative_cache = WeakSet()\r
- cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter\r
- elif subclass in cls._abc_negative_cache:\r
- return False\r
- # Check the subclass hook\r
- ok = cls.__subclasshook__(subclass)\r
- if ok is not NotImplemented:\r
- assert isinstance(ok, bool)\r
- if ok:\r
- cls._abc_cache.add(subclass)\r
- else:\r
- cls._abc_negative_cache.add(subclass)\r
- return ok\r
- # Check if it's a direct subclass\r
- if cls in getattr(subclass, '__mro__', ()):\r
- cls._abc_cache.add(subclass)\r
- return True\r
- # Check if it's a subclass of a registered class (recursive)\r
- for rcls in cls._abc_registry:\r
- if issubclass(subclass, rcls):\r
- cls._abc_cache.add(subclass)\r
- return True\r
- # Check if it's a subclass of a subclass (recursive)\r
- for scls in cls.__subclasses__():\r
- if issubclass(subclass, scls):\r
- cls._abc_cache.add(subclass)\r
- return True\r
- # No dice; update negative cache\r
- cls._abc_negative_cache.add(subclass)\r
- return False\r