[Python-checkins] r55277 - sandbox/trunk/abc/def.py sandbox/trunk/abc/xyz.py

guido.van.rossum python-checkins at python.org
Fri May 11 22:53:22 CEST 2007


Author: guido.van.rossum
Date: Fri May 11 22:53:14 2007
New Revision: 55277

Modified:
   sandbox/trunk/abc/def.py
   sandbox/trunk/abc/xyz.py
Log:
xyz.py: merged in @abstractmethod from abc.py; now matches PEP 3119.
def.py: updated from PEP 3119.


Modified: sandbox/trunk/abc/def.py
==============================================================================
--- sandbox/trunk/abc/def.py	(original)
+++ sandbox/trunk/abc/def.py	Fri May 11 22:53:14 2007
@@ -1,4 +1,4 @@
-class ABC(type):
+class ACBMeta(type):
 
     def __instancecheck__(cls, inst):
         """Implement isinstance(inst, cls)."""
@@ -10,13 +10,13 @@
         candidates = cls.__dict__.get("__subclass__", set()) | {cls}
         return any(c in candidates for c in sub.mro())
 
-class Sequence(metaclass=ABC):
+class Sequence(metaclass=ACBMeta):
     __subclass__ = {list, tuple}
 
 assert issubclass(list, Sequence)
 assert issubclass(tuple, Sequence)
 
-class AppendableSequence(Sequence, metaclass=ABC):
+class AppendableSequence(Sequence):
     __subclass__ = {list}
 
 assert issubclass(list, AppendableSequence)

Modified: sandbox/trunk/abc/xyz.py
==============================================================================
--- sandbox/trunk/abc/xyz.py	(original)
+++ sandbox/trunk/abc/xyz.py	Fri May 11 22:53:14 2007
@@ -6,6 +6,56 @@
 __all__ = ["ABCMeta"]
 
 
+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 the normal
+    'super' call mechanisms.
+
+    Usage:
+
+        class C(metaclass=ABCMeta):
+            @abstractmethod
+            def my_abstract_method(self, ...):
+                ...
+    """
+    funcobj.__isabstractmethod__ = True
+    return funcobj
+
+
+class _Abstract(object):
+
+    """Helper class inserted into the bases by ABCMeta (using _fix_bases()).
+
+    You should never need to explicitly subclass this class.
+    """
+
+    def __new__(cls, *args, **kwds):
+        am = cls.__dict__.get("__abstractmethods__")
+        if am:
+            raise TypeError("can't instantiate abstract class %s "
+                            "with abstract methods %s" %
+                            (cls.__name__, ", ".join(sorted(am))))
+        return super(_Abstract, cls).__new__(cls, *args, **kwds)
+
+
+def _fix_bases(bases):
+    """Helper method that inserts _Abstract in the bases if needed."""
+    for base in bases:
+        if issubclass(base, _Abstract):
+            # _Abstract is already a base (maybe indirectly)
+            return bases
+    if object in bases:
+        # Replace object with _Abstract
+        return tuple([_Abstract if base is object else base
+                      for base in bases])
+    # Append _Abstract to the end
+    return bases + (_Abstract,)
+
+
 class ABCMeta(type):
 
     """Metaclass for defining Abstract Base Classes (ABCs).
@@ -28,7 +78,19 @@
     __invalidation_counter = 0
 
     def __new__(mcls, name, bases, namespace):
+        bases = _fix_bases(bases)
         cls = super(ABCMeta, mcls).__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__ = abstracts
+        # Set up inheritance registry
         cls.__abc_registry__ = set()
         cls.__abc_cache__ = set()
         cls.__abc_negative_cache__ = set()
@@ -37,8 +99,13 @@
 
     def register(cls, subclass):
         """Register a virtual subclass of an ABC."""
+        if not isinstance(cls, type):
+            raise TypeError("Can only register classes")
         if issubclass(subclass, cls):
             return  # Already a subclass
+        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.__invalidation_counter += 1  # Invalidate negative cache
 
@@ -159,6 +226,20 @@
         show(abc)
         abc._dump_registry()
 
+    class A(metaclass=ABCMeta):
+        @abstractmethod
+        def foo(self): pass
+    class C(A):
+        def foo(self): pass
+
+    try:
+        A()
+    except TypeError as err:
+        print("as expected:", err)
+    else:
+        assert False
+    C()
+
 
 if __name__ == "__main__":
     _demo()


More information about the Python-checkins mailing list