Kaydet (Commit) d474f3a2 authored tarafından Florent Xicluna's avatar Florent Xicluna

Merged revisions 78800 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r78800 | florent.xicluna | 2010-03-08 16:20:28 +0100 (lun, 08 mar 2010) | 2 lines

  #7624: Fix isinstance(foo(), collections.Callable) for old-style classes.
........
üst dbac1689
...@@ -21,6 +21,14 @@ __all__ = ["Hashable", "Iterable", "Iterator", ...@@ -21,6 +21,14 @@ __all__ = ["Hashable", "Iterable", "Iterator",
### ONE-TRICK PONIES ### ### 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: class Hashable:
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
...@@ -31,11 +39,16 @@ class Hashable: ...@@ -31,11 +39,16 @@ class Hashable:
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Hashable: if cls is Hashable:
for B in C.__mro__: try:
if "__hash__" in B.__dict__: for B in C.__mro__:
if B.__dict__["__hash__"]: if "__hash__" in B.__dict__:
return True if B.__dict__["__hash__"]:
break return True
break
except AttributeError:
# Old-style class
if getattr(C, "__hash__", None):
return True
return NotImplemented return NotImplemented
...@@ -50,7 +63,7 @@ class Iterable: ...@@ -50,7 +63,7 @@ class Iterable:
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Iterable: if cls is Iterable:
if any("__iter__" in B.__dict__ for B in C.__mro__): if _hasattr(C, "__iter__"):
return True return True
return NotImplemented return NotImplemented
...@@ -69,7 +82,7 @@ class Iterator(Iterable): ...@@ -69,7 +82,7 @@ class Iterator(Iterable):
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Iterator: if cls is Iterator:
if any("next" in B.__dict__ for B in C.__mro__): if _hasattr(C, "next"):
return True return True
return NotImplemented return NotImplemented
...@@ -84,7 +97,7 @@ class Sized: ...@@ -84,7 +97,7 @@ class Sized:
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Sized: if cls is Sized:
if any("__len__" in B.__dict__ for B in C.__mro__): if _hasattr(C, "__len__"):
return True return True
return NotImplemented return NotImplemented
...@@ -99,7 +112,7 @@ class Container: ...@@ -99,7 +112,7 @@ class Container:
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Container: if cls is Container:
if any("__contains__" in B.__dict__ for B in C.__mro__): if _hasattr(C, "__contains__"):
return True return True
return NotImplemented return NotImplemented
...@@ -114,7 +127,7 @@ class Callable: ...@@ -114,7 +127,7 @@ class Callable:
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Callable: if cls is Callable:
if any("__call__" in B.__dict__ for B in C.__mro__): if _hasattr(C, "__call__"):
return True return True
return NotImplemented return NotImplemented
......
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
"""Abstract Base Classes (ABCs) according to PEP 3119.""" """Abstract Base Classes (ABCs) according to PEP 3119."""
# Instance of old-style class
class _C: pass
_InstanceType = type(_C())
def abstractmethod(funcobj): def abstractmethod(funcobj):
"""A decorator indicating abstract methods. """A decorator indicating abstract methods.
...@@ -124,6 +129,9 @@ class ABCMeta(type): ...@@ -124,6 +129,9 @@ class ABCMeta(type):
if subclass in cls._abc_cache: if subclass in cls._abc_cache:
return True return True
subtype = type(instance) subtype = type(instance)
# Old-style instances
if subtype is _InstanceType:
subtype = subclass
if subtype is subclass or subclass is None: if subtype is subclass or subclass is None:
if (cls._abc_negative_cache_version == if (cls._abc_negative_cache_version ==
ABCMeta._abc_invalidation_counter and ABCMeta._abc_invalidation_counter and
......
...@@ -210,6 +210,27 @@ class ABCTestCase(unittest.TestCase): ...@@ -210,6 +210,27 @@ class ABCTestCase(unittest.TestCase):
C = type('C', (abc,), stubs) C = type('C', (abc,), stubs)
self.assertRaises(TypeError, C, name) 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.assertTrue(isinstance(C(), abc))
self.assertTrue(issubclass(C, abc))
# old-style class
class C: pass
setattr(C, name, stub)
self.assertTrue(isinstance(C(), abc))
self.assertTrue(issubclass(C, abc))
# new-style class
C = type('C', (object,), {'__hash__': None})
self.assertFalse(isinstance(C(), abc))
self.assertFalse(issubclass(C, abc))
# old-style class
class C: pass
self.assertFalse(isinstance(C(), abc))
self.assertFalse(issubclass(C, abc))
class TestOneTrickPonyABCs(ABCTestCase): class TestOneTrickPonyABCs(ABCTestCase):
...@@ -238,6 +259,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -238,6 +259,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.assertEqual(hash(H()), 0) self.assertEqual(hash(H()), 0)
self.failIf(issubclass(int, H)) self.failIf(issubclass(int, H))
self.validate_abstract_methods(Hashable, '__hash__') self.validate_abstract_methods(Hashable, '__hash__')
self.validate_isinstance(Hashable, '__hash__')
def test_Iterable(self): def test_Iterable(self):
# Check some non-iterables # Check some non-iterables
...@@ -262,6 +284,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -262,6 +284,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.assertEqual(list(I()), []) self.assertEqual(list(I()), [])
self.failIf(issubclass(str, I)) self.failIf(issubclass(str, I))
self.validate_abstract_methods(Iterable, '__iter__') self.validate_abstract_methods(Iterable, '__iter__')
self.validate_isinstance(Iterable, '__iter__')
def test_Iterator(self): def test_Iterator(self):
non_samples = [None, 42, 3.14, 1j, "".encode('ascii'), "", (), [], non_samples = [None, 42, 3.14, 1j, "".encode('ascii'), "", (), [],
...@@ -281,6 +304,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -281,6 +304,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.failUnless(isinstance(x, Iterator), repr(x)) self.failUnless(isinstance(x, Iterator), repr(x))
self.failUnless(issubclass(type(x), Iterator), repr(type(x))) self.failUnless(issubclass(type(x), Iterator), repr(type(x)))
self.validate_abstract_methods(Iterator, 'next') self.validate_abstract_methods(Iterator, 'next')
self.validate_isinstance(Iterator, 'next')
def test_Sized(self): def test_Sized(self):
non_samples = [None, 42, 3.14, 1j, non_samples = [None, 42, 3.14, 1j,
...@@ -298,6 +322,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -298,6 +322,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.failUnless(isinstance(x, Sized), repr(x)) self.failUnless(isinstance(x, Sized), repr(x))
self.failUnless(issubclass(type(x), Sized), repr(type(x))) self.failUnless(issubclass(type(x), Sized), repr(type(x)))
self.validate_abstract_methods(Sized, '__len__') self.validate_abstract_methods(Sized, '__len__')
self.validate_isinstance(Sized, '__len__')
def test_Container(self): def test_Container(self):
non_samples = [None, 42, 3.14, 1j, non_samples = [None, 42, 3.14, 1j,
...@@ -315,6 +340,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -315,6 +340,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.failUnless(isinstance(x, Container), repr(x)) self.failUnless(isinstance(x, Container), repr(x))
self.failUnless(issubclass(type(x), Container), repr(type(x))) self.failUnless(issubclass(type(x), Container), repr(type(x)))
self.validate_abstract_methods(Container, '__contains__') self.validate_abstract_methods(Container, '__contains__')
self.validate_isinstance(Container, '__contains__')
def test_Callable(self): def test_Callable(self):
non_samples = [None, 42, 3.14, 1j, non_samples = [None, 42, 3.14, 1j,
...@@ -334,6 +360,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -334,6 +360,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.failUnless(isinstance(x, Callable), repr(x)) self.failUnless(isinstance(x, Callable), repr(x))
self.failUnless(issubclass(type(x), Callable), repr(type(x))) self.failUnless(issubclass(type(x), Callable), repr(type(x)))
self.validate_abstract_methods(Callable, '__call__') self.validate_abstract_methods(Callable, '__call__')
self.validate_isinstance(Callable, '__call__')
def test_direct_subclassing(self): def test_direct_subclassing(self):
for B in Hashable, Iterable, Iterator, Sized, Container, Callable: for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
...@@ -475,7 +502,7 @@ class TestCollectionABCs(ABCTestCase): ...@@ -475,7 +502,7 @@ class TestCollectionABCs(ABCTestCase):
self.validate_abstract_methods(MutableSequence, '__contains__', '__iter__', self.validate_abstract_methods(MutableSequence, '__contains__', '__iter__',
'__len__', '__getitem__', '__setitem__', '__delitem__', 'insert') '__len__', '__getitem__', '__setitem__', '__delitem__', 'insert')
import doctest, collections import collections
def test_main(verbose=None): def test_main(verbose=None):
NamedTupleDocs = doctest.DocTestSuite(module=collections) NamedTupleDocs = doctest.DocTestSuite(module=collections)
......
...@@ -23,6 +23,9 @@ Core and Builtins ...@@ -23,6 +23,9 @@ Core and Builtins
Library Library
------- -------
- Issue #7624: Fix isinstance(foo(), collections.Callable) for old-style
classes.
- Issue #7512: shutil.copystat() could raise an OSError when the filesystem - Issue #7512: shutil.copystat() could raise an OSError when the filesystem
didn't support chflags() (for example ZFS under FreeBSD). The error is didn't support chflags() (for example ZFS under FreeBSD). The error is
now silenced. now silenced.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment