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

guido.van.rossum python-checkins at python.org
Fri May 11 02:56:27 CEST 2007


Author: guido.van.rossum
Date: Fri May 11 02:56:23 2007
New Revision: 55239

Added:
   sandbox/trunk/abc/xyz.py   (contents, props changed)
Log:
Checkpoint.


Added: sandbox/trunk/abc/xyz.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/abc/xyz.py	Fri May 11 02:56:23 2007
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3.0
+"""Abstract Base Classes, reloaded."""
+
+
+import sys
+
+
+def closure(function, roots):
+    """Compute the closure of a function over a set of roots."""
+    more = roots
+    result = set()
+    while more:
+        result.update(more)
+        todo = set()
+        for x in more:
+            for y in function(x):
+                if y not in result:
+                    todo.add(y)
+        more = todo
+    return result
+
+
+def subclasses(classes):
+    """Compute the closure of the __subclasses__ method over classes."""
+    return closure(lambda c: c.__subclasses__(), classes)
+
+
+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.
+    __invalidation_counter = 0
+
+    def __new__(mcls, name, bases, namespace):
+        cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
+        cls.__abc_registry__ = set()
+        cls.__abc_cache__ = set()
+        cls.__abc_negative_cache__ = set()
+        cls.__abc_negative_cache_version__ = ABCMeta.__invalidation_counter
+        return cls
+
+    def register(cls, subclass):
+        """Register a virtual subclass of an ABC."""
+        cls.__abc_registry__.add(subclass)
+        ABCMeta.__invalidation_counter += 1  # Invalidate negative cache
+
+    def _dump_registry(cls, file=None):
+        """Debug helper to print the ABC registry."""
+        if file is None:
+            file = sys.stdout
+        print("Class: %s.%s" % (cls.__module__, cls.__name__), file=file)
+        print("Inv.counter: %s" % ABCMeta.__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)."""
+        return any(cls.__subclasscheck__(c)
+                   for c in {instance.__class__, type(instance)})
+
+    def __subclasscheck__(cls, subclass):
+        """Override for issubclass(subclass, cls)."""
+        # XXX I KNOW THIS IS WRONG
+        # Check cache
+        if subclass in cls.__abc_cache__:
+            return True
+        # Check negative cache; may have to invalidate
+        if cls.__abc_negative_cache_version__ < ABCMeta.__invalidation_counter:
+            # Invalidate the negative cache
+            cls.__abc_negative_cache_version__ = ABCMeta.__invalidation_counter
+            cls.__abc_negative_cache__ = set()
+        elif subclass in cls.__abc_negative_cache__:
+            return False
+        # Check for direct subclass
+        mro = subclass.mro()
+        if cls in mro:
+            cls.__abc_cache__.add(subclass)
+            return True
+        # Check registry
+        for base in mro:
+                if base in cls.__abc_registry__:
+                    cls.__abc_cache__.add(subclass)
+                    cls.__abc_cache__.add(base)
+                    return True
+        # XXX Should really check all the registered classes too...
+        cls.__abc_negative_cache__.add(subclass)
+        return False
+
+
+class Sequence(metaclass=ABCMeta):
+    """A sequence without any behavior implementation."""
+
+Sequence.register(tuple)
+Sequence.register(str)
+Sequence.register(unicode)
+
+class Mapping(metaclass=ABCMeta):
+    """A mapping without any behavior implementation."""
+
+Mapping.register(dict)
+
+class Set(metaclass=ABCMeta):
+    """A set without any behavior implementation."""
+
+Set.register(set)
+Set.register(frozenset)
+
+class MutableSequence(Sequence):
+    """A mutable sequence."""
+
+MutableSequence.register(list)
+MutableSequence.register(bytes)
+
+Sequence.register(MutableSequence)
+
+SAMPLES = [0, b"", "", u"", [], (), {}, set()]
+for abc in Sequence, Mapping, Set, MutableSequence:
+    print("%s: %s" % (abc.__name__,
+                      ", ".join([repr(sample)
+                                 for sample in SAMPLES
+                                 if isinstance(sample, abc)])))


More information about the Python-checkins mailing list