Index: Lib/abc.py =================================================================== --- Lib/abc.py (revision 77822) +++ Lib/abc.py (working copy) @@ -4,6 +4,11 @@ """Abstract Base Classes (ABCs) according to PEP 3119.""" +# Instance of old-style class +class _C: pass +_InstanceType = type(_C()) + + def abstractmethod(funcobj): """A decorator indicating abstract methods. @@ -124,6 +129,9 @@ if subclass in cls._abc_cache: return True subtype = type(instance) + # Old-style instances + if subtype == _InstanceType: + subtype = subclass if subtype is subclass or subclass is None: if (cls._abc_negative_cache_version == ABCMeta._abc_invalidation_counter and Index: Lib/_abcoll.py =================================================================== --- Lib/_abcoll.py (revision 77822) +++ Lib/_abcoll.py (working copy) @@ -21,6 +21,14 @@ ### ONE-TRICK PONIES ### +def _hasattr(C, attr): + try: + return any(attr in B.__dict__ for B in C.__mro__) + except AttributeError: + # Old-style class + return hasattr(C, attr) + + class Hashable: __metaclass__ = ABCMeta @@ -31,11 +39,16 @@ @classmethod def __subclasshook__(cls, C): if cls is Hashable: - for B in C.__mro__: - if "__hash__" in B.__dict__: - if B.__dict__["__hash__"]: - return True - break + try: + for B in C.__mro__: + if "__hash__" in B.__dict__: + if B.__dict__["__hash__"]: + return True + break + except AttributeError: + # Old-style class + if getattr(C, "__hash__", None): + return True return NotImplemented @@ -50,7 +63,7 @@ @classmethod def __subclasshook__(cls, C): if cls is Iterable: - if any("__iter__" in B.__dict__ for B in C.__mro__): + if _hasattr(C, "__iter__"): return True return NotImplemented @@ -69,7 +82,7 @@ @classmethod def __subclasshook__(cls, C): if cls is Iterator: - if any("next" in B.__dict__ for B in C.__mro__): + if _hasattr(C, "next"): return True return NotImplemented @@ -84,7 +97,7 @@ @classmethod def __subclasshook__(cls, C): if cls is Sized: - if any("__len__" in B.__dict__ for B in C.__mro__): + if _hasattr(C, "__len__"): return True return NotImplemented @@ -99,7 +112,7 @@ @classmethod def __subclasshook__(cls, C): if cls is Container: - if any("__contains__" in B.__dict__ for B in C.__mro__): + if _hasattr(C, "__contains__"): return True return NotImplemented @@ -114,7 +127,7 @@ @classmethod def __subclasshook__(cls, C): if cls is Callable: - if any("__call__" in B.__dict__ for B in C.__mro__): + if _hasattr(C, "__call__"): return True return NotImplemented Index: Lib/test/test_collections.py =================================================================== --- Lib/test/test_collections.py (revision 77822) +++ Lib/test/test_collections.py (working copy) @@ -226,6 +226,27 @@ C = type('C', (abc,), stubs) self.assertRaises(TypeError, C, name) + def validate_isinstance(self, abc, name): + stub = lambda s, *args: 0 + + # new-style class + C = type('C', (object,), {name: stub}) + self.assertIsInstance(C(), abc) + self.assertTrue(issubclass(C, abc)) + # old-style class + class C: pass + setattr(C, name, stub) + self.assertIsInstance(C(), abc) + self.assertTrue(issubclass(C, abc)) + + # new-style class + C = type('C', (object,), {'__hash__': None}) + self.assertNotIsInstance(C(), abc) + self.assertFalse(issubclass(C, abc)) + # old-style class + class C: pass + self.assertNotIsInstance(C(), abc) + self.assertFalse(issubclass(C, abc)) class TestOneTrickPonyABCs(ABCTestCase): @@ -254,6 +275,7 @@ self.assertEqual(hash(H()), 0) self.assertFalse(issubclass(int, H)) self.validate_abstract_methods(Hashable, '__hash__') + self.validate_isinstance(Hashable, '__hash__') def test_Iterable(self): # Check some non-iterables @@ -278,6 +300,7 @@ self.assertEqual(list(I()), []) self.assertFalse(issubclass(str, I)) self.validate_abstract_methods(Iterable, '__iter__') + self.validate_isinstance(Iterable, '__iter__') def test_Iterator(self): non_samples = [None, 42, 3.14, 1j, "".encode('ascii'), "", (), [], @@ -297,6 +320,7 @@ self.assertIsInstance(x, Iterator) self.assertTrue(issubclass(type(x), Iterator), repr(type(x))) self.validate_abstract_methods(Iterator, 'next') + self.validate_isinstance(Iterator, 'next') def test_Sized(self): non_samples = [None, 42, 3.14, 1j, @@ -314,6 +338,7 @@ self.assertIsInstance(x, Sized) self.assertTrue(issubclass(type(x), Sized), repr(type(x))) self.validate_abstract_methods(Sized, '__len__') + self.validate_isinstance(Sized, '__len__') def test_Container(self): non_samples = [None, 42, 3.14, 1j, @@ -331,6 +356,7 @@ self.assertIsInstance(x, Container) self.assertTrue(issubclass(type(x), Container), repr(type(x))) self.validate_abstract_methods(Container, '__contains__') + self.validate_isinstance(Container, '__contains__') def test_Callable(self): non_samples = [None, 42, 3.14, 1j, @@ -350,6 +376,7 @@ self.assertIsInstance(x, Callable) self.assertTrue(issubclass(type(x), Callable), repr(type(x))) self.validate_abstract_methods(Callable, '__call__') + self.validate_isinstance(Callable, '__call__') def test_direct_subclassing(self): for B in Hashable, Iterable, Iterator, Sized, Container, Callable: