[Python-3000-checkins] r55838 - in python/branches/p3yk/Lib: collections.py test/test_collections.py

guido.van.rossum python-3000-checkins at python.org
Sat Jun 9 02:38:59 CEST 2007


Author: guido.van.rossum
Date: Sat Jun  9 02:38:55 2007
New Revision: 55838

Modified:
   python/branches/p3yk/Lib/collections.py
   python/branches/p3yk/Lib/test/test_collections.py
Log:
Implement part of PEP 3119 -- One Trick Ponies.


Modified: python/branches/p3yk/Lib/collections.py
==============================================================================
--- python/branches/p3yk/Lib/collections.py	(original)
+++ python/branches/p3yk/Lib/collections.py	Sat Jun  9 02:38:55 2007
@@ -1,8 +1,11 @@
-__all__ = ['deque', 'defaultdict', 'NamedTuple']
+__all__ = ['deque', 'defaultdict', 'NamedTuple',
+           'Hashable', 'Iterable', 'Iterator', 'Sized', 'Container',
+           ]
 
 from _collections import deque, defaultdict
 from operator import itemgetter as _itemgetter
 import sys as _sys
+from abc import abstractmethod as _abstractmethod, ABCMeta as _ABCMeta
 
 def NamedTuple(typename, s):
     """Returns a new subclass of tuple with named fields.
@@ -46,7 +49,79 @@
     return result
 
 
+class _OneTrickPony(metaclass=_ABCMeta):
 
+    @classmethod
+    def __instancecheck__(cls, x):
+        return issubclass(x.__class__, cls)
+
+    @classmethod
+    def register(cls, C):
+        raise TypeError("class %s doesn't allow registration of subclasses" %
+                        cls.__name__)
+
+
+class Hashable(_OneTrickPony):
+
+    @_abstractmethod
+    def __hash__(self):
+        return 0
+
+    @classmethod
+    def __subclasscheck__(cls, C):
+        for B in C.__mro__:
+            if "__hash__" in B.__dict__:
+                return B.__dict__["__hash__"] is not None
+        return False
+
+
+class Iterable(_OneTrickPony):
+
+    @_abstractmethod
+    def __iter__(self):
+        while False:
+            yield None
+
+    @classmethod
+    def __subclasscheck__(cls, C):
+        return any("__iter__" in B.__dict__ or "__getitem__" in B.__dict__
+                   for B in C.__mro__)
+
+
+class Iterator(_OneTrickPony):
+
+    @_abstractmethod
+    def __next__(self):
+        raise StopIteration
+
+    def __iter__(self):
+        return self
+
+    @classmethod
+    def __subclasscheck__(cls, C):
+        return any("__next__" in B.__dict__ for B in C.__mro__)
+
+
+class Sized(_OneTrickPony):
+
+    @_abstractmethod
+    def __len__(self):
+        return 0
+
+    @classmethod
+    def __subclasscheck__(cls, C):
+        return any("__len__" in B.__dict__ for B in C.__mro__)
+
+
+class Container(_OneTrickPony):
+
+    @_abstractmethod
+    def __contains__(self, x):
+        return False
+
+    @classmethod
+    def __subclasscheck__(cls, C):
+        return any("__contains__" in B.__dict__ for B in C.__mro__)
 
 
 if __name__ == '__main__':

Modified: python/branches/p3yk/Lib/test/test_collections.py
==============================================================================
--- python/branches/p3yk/Lib/test/test_collections.py	(original)
+++ python/branches/p3yk/Lib/test/test_collections.py	Sat Jun  9 02:38:55 2007
@@ -1,6 +1,10 @@
+"""Unit tests for collections.py."""
+
 import unittest
 from test import test_support
 from collections import NamedTuple
+from collections import Hashable, Iterable, Iterator, Sized, Container
+
 
 class TestNamedTuple(unittest.TestCase):
 
@@ -51,9 +55,89 @@
         self.assertRaises(AttributeError, eval, 'p.z', locals())
 
 
+class TestABCs(unittest.TestCase):
+
+    def test_Hashable(self):
+        # Check some non-hashables
+        non_samples = [bytes(), list(), set(), dict()]
+        for x in non_samples:
+            self.failIf(isinstance(x, Hashable), repr(x))
+            self.failIf(issubclass(type(x), Hashable), repr(type(x)))
+        # Check some hashables
+        samples = [None,
+                   int(), float(), complex(),
+                   str(), unicode(),
+                   tuple(), frozenset(),
+                   int, list, object, type,
+                   ]
+        for x in samples:
+            self.failUnless(isinstance(x, Hashable), repr(x))
+            self.failUnless(issubclass(type(x), Hashable), repr(type(x)))
+        self.assertRaises(TypeError, Hashable)
+        # Check direct subclassing
+        class H(Hashable):
+            def __hash__(self):
+                return super(H, self).__hash__()
+        self.assertEqual(hash(H()), 0)
+        # Check registration is disabled
+        class C:
+            def __hash__(self):
+                return 0
+        self.assertRaises(TypeError, Hashable.register, C)
+
+    def test_Iterable(self):
+        non_samples = [None, 42, 3.14, 1j]
+        for x in non_samples:
+            self.failIf(isinstance(x, Iterable), repr(x))
+            self.failIf(issubclass(type(x), Iterable), repr(type(x)))
+        samples = [bytes(), str(), unicode(),
+                   tuple(), list(), set(), frozenset(), dict(),
+                   ]
+        for x in samples:
+            self.failUnless(isinstance(x, Iterable), repr(x))
+            self.failUnless(issubclass(type(x), Iterable), repr(type(x)))
+
+    def test_Iterator(self):
+        non_samples = [None, 42, 3.14, 1j, b"", "", u"", (), [], {}, set()]
+        for x in non_samples:
+            self.failIf(isinstance(x, Iterator), repr(x))
+            self.failIf(issubclass(type(x), Iterator), repr(type(x)))
+        samples = [iter(bytes()), iter(str()), iter(unicode()),
+                   iter(tuple()), iter(list()), iter(dict()),
+                   iter(set()), iter(frozenset()),
+                   ]
+        for x in samples:
+            self.failUnless(isinstance(x, Iterator), repr(x))
+            self.failUnless(issubclass(type(x), Iterator), repr(type(x)))
+
+    def test_Sized(self):
+        non_samples = [None, 42, 3.14, 1j]
+        for x in non_samples:
+            self.failIf(isinstance(x, Sized), repr(x))
+            self.failIf(issubclass(type(x), Sized), repr(type(x)))
+        samples = [bytes(), str(), unicode(),
+                   tuple(), list(), set(), frozenset(), dict(),
+                   ]
+        for x in samples:
+            self.failUnless(isinstance(x, Sized), repr(x))
+            self.failUnless(issubclass(type(x), Sized), repr(type(x)))
+
+    def test_Container(self):
+        non_samples = [None, 42, 3.14, 1j]
+        for x in non_samples:
+            self.failIf(isinstance(x, Container), repr(x))
+            self.failIf(issubclass(type(x), Container), repr(type(x)))
+        samples = [bytes(), str(), unicode(),
+                   tuple(), list(), set(), frozenset(), dict(),
+                   ]
+        for x in samples:
+            self.failUnless(isinstance(x, Container), repr(x))
+            self.failUnless(issubclass(type(x), Container), repr(type(x)))
+
+
 def test_main(verbose=None):
     import collections as CollectionsModule
-    test_classes = [TestNamedTuple]
+    test_classes = [TestNamedTuple, TestABCs]
     test_support.run_unittest(*test_classes)
     test_support.run_doctest(CollectionsModule, verbose)
 


More information about the Python-3000-checkins mailing list