[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