mirror of
https://github.com/python/cpython.git
synced 2025-08-16 06:40:56 +00:00
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. ........
This commit is contained in:
parent
dbac168948
commit
d474f3a239
4 changed files with 62 additions and 11 deletions
|
@ -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:
|
||||||
@classmethod
|
@classmethod
|
||||||
def __subclasshook__(cls, C):
|
def __subclasshook__(cls, C):
|
||||||
if cls is Hashable:
|
if cls is Hashable:
|
||||||
|
try:
|
||||||
for B in C.__mro__:
|
for B in C.__mro__:
|
||||||
if "__hash__" in B.__dict__:
|
if "__hash__" in B.__dict__:
|
||||||
if B.__dict__["__hash__"]:
|
if B.__dict__["__hash__"]:
|
||||||
return True
|
return True
|
||||||
break
|
break
|
||||||
|
except AttributeError:
|
||||||
|
# Old-style class
|
||||||
|
if getattr(C, "__hash__", None):
|
||||||
|
return True
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
@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:
|
||||||
@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:
|
||||||
@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:
|
||||||
@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 @@
|
||||||
"""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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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
|
||||||
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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue