[Python-checkins] r54638 - sandbox/trunk/abc/abc.py sandbox/trunk/abc/test_abc.py
guido.van.rossum
python-checkins at python.org
Sat Mar 31 22:27:52 CEST 2007
Author: guido.van.rossum
Date: Sat Mar 31 22:27:51 2007
New Revision: 54638
Added:
sandbox/trunk/abc/test_abc.py (contents, props changed)
Modified:
sandbox/trunk/abc/abc.py
Log:
Create some unit tests. Add some docs to the abstractmethod decorator.
Modified: sandbox/trunk/abc/abc.py
==============================================================================
--- sandbox/trunk/abc/abc.py (original)
+++ sandbox/trunk/abc/abc.py Sat Mar 31 22:27:51 2007
@@ -3,8 +3,8 @@
"""Abstract Base Classes experiment.
Note: this depends on the brand new Py3k feature that object.__ne__()
-is implemented by calling object.__eq__() and reversing the outcome
-(unless NotImplemented).
+is implemented by calling __eq__() and reversing the outcome (unless
+NotImplemented).
XXX Should we use argument annotations here?
@@ -18,27 +18,73 @@
def abstractmethod(funcobj):
- """A decorator indicating abstract methods."""
+ """A decorator indicating abstract methods.
+
+ Requires that the class (directly or indirectly) derives from
+ Abstract, and that the metaclass is AbstractClass or derived from it
+ (deriving from Abstract ensure this). A class deriving from
+ Abstract 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(Abstract):
+ @abstractmethod
+ def my_abstract_method(self, ...):
+ ...
+
+ When combining this with other decorators, this should come last:
+
+ class C(Abstract):
+ @classmethod
+ @abstractmethod
+ def my_abstract_class_method(self, ...):
+ ...
+ """
funcobj.__abstractmethod__ = True
return funcobj
class AbstractClass(type):
+ """Metaclass to support the abstractmethod decorator."""
+
def __new__(mcls, name, bases, namespace):
- obj = super(AbstractClass, mcls).__new__(mcls, name, bases, namespace)
+ cls = super(AbstractClass, mcls).__new__(mcls, name, bases, namespace)
abstracts = set()
for base in bases:
abstracts.update(getattr(base, "__abstractmethods__", set()))
for name, value in namespace.items():
if getattr(value, "__abstractmethod__", False):
abstracts.add(name)
- obj.__abstractmethods__ = abstracts
- return obj
+ cls.__abstractmethods__ = abstracts
+ return cls
+
+
+class AbstractInstantiationError(TypeError):
+
+ """Exception raised when an abstract class is instantiated."""
+
+ def __init__(self, abstract_methods):
+ TypeError.__init__(self)
+ self.abstract_methods = abstract_methods
+
+ def __str__(self):
+ msg = ", ".join(sorted(self.abstract_methods))
+ return "Can't instantiate class with abstract method(s) %s" % msg
+
+ def __repr__(self):
+ return "AbstractInstantiationError(%r)" % (self.abstract_methods,)
class Abstract(metaclass=AbstractClass):
+ """Base class to support the abstractmethod decorator.
+
+ This implicitly sets the metaclass to AbstractClass.
+ """
+
def __new__(cls):
bad = set()
for name in cls.__abstractmethods__:
@@ -46,8 +92,7 @@
if getattr(value, "__abstractmethod__", False):
bad.add(name)
if bad:
- raise TypeError("Can't instantiate class with abstract methods %s" %
- ", ".join(sorted(bad)))
+ raise AbstractInstantiationError(bad)
return super(Abstract, cls).__new__(cls)
@@ -499,29 +544,3 @@
if h == -1:
h = -2
return h
-
-
-### test ###
-
-
-def _test():
- # Test that HashableSequence.__hash__() emulates tuple.__hash__().
- class C(HashableSequence):
- def __new__(cls, values):
- obj = super(C, cls).__new__(cls)
- obj.__values = list(values)
- return obj
- def __len__(self):
- return len(self.__values)
- def __getitem__(self, i):
- return self.__values[i]
- for l in ([], [0], [1], [0, 1],
- list(range(-sys.maxint, sys.maxint, 100000)),
- "The quick brown fox jumps over the lazy dog".split()):
- a = C(l)
- ha = hash(a)
- htl = hash(tuple(l))
- assert ha == htl, (l, ha, htl)
-
-if __name__ == "__main__":
- _test()
Added: sandbox/trunk/abc/test_abc.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/abc/test_abc.py Sat Mar 31 22:27:51 2007
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3.0
+"""Unit tests for abc.py."""
+
+import sys
+import unittest
+
+import abc
+
+class ABCTestCase(unittest.TestCase):
+
+ def test_abstract_method_machinery(self):
+ class C(abc.Abstract):
+ @abc.abstractmethod
+ def foo(self): pass
+ def bar(self): pass
+ self.assertRaises(abc.AbstractInstantiationError, C)
+ class D(C):
+ def bar(self): pass
+ self.assertRaises(abc.AbstractInstantiationError, D)
+ class E(D):
+ def foo(self): pass
+ E()
+
+ def test_hashable_sequence_hash_matches_tuple_hash(self):
+ class C(abc.HashableSequence):
+ def __new__(cls, values):
+ obj = super(C, cls).__new__(cls)
+ obj.__values = list(values)
+ return obj
+ def __len__(self):
+ return len(self.__values)
+ def __getitem__(self, i):
+ return self.__values[i]
+ for l in ([], [0], [1], [0, 1],
+ list(range(-sys.maxint, sys.maxint, 100000)),
+ "The quick brown fox jumps over the lazy dog".split()):
+ hcl = hash(C(l))
+ htl = hash(tuple(l))
+ self.assertEqual(hcl, htl, repr((l, hcl, htl)))
+
+
+if __name__ == "__main__":
+ unittest.main()
More information about the Python-checkins
mailing list