[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