Kaydet (Commit) 4054b172 authored tarafından Aaron Hall, MBA's avatar Aaron Hall, MBA Kaydeden (comit) Serhiy Storchaka

bpo-26103: Fix inspect.isdatadescriptor() and a data descriptor definition. (GH-1959)

Look for '__set__' or '__delete__'.
üst aef639f6
...@@ -58,7 +58,7 @@ That is all there is to it. Define any of these methods and an object is ...@@ -58,7 +58,7 @@ That is all there is to it. Define any of these methods and an object is
considered a descriptor and can override default behavior upon being looked up considered a descriptor and can override default behavior upon being looked up
as an attribute. as an attribute.
If an object defines both :meth:`__get__` and :meth:`__set__`, it is considered If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered
a data descriptor. Descriptors that only define :meth:`__get__` are called a data descriptor. Descriptors that only define :meth:`__get__` are called
non-data descriptors (they are typically used for methods but other uses are non-data descriptors (they are typically used for methods but other uses are
possible). possible).
......
...@@ -110,7 +110,7 @@ def ismethoddescriptor(object): ...@@ -110,7 +110,7 @@ def ismethoddescriptor(object):
def isdatadescriptor(object): def isdatadescriptor(object):
"""Return true if the object is a data descriptor. """Return true if the object is a data descriptor.
Data descriptors have both a __get__ and a __set__ attribute. Examples are Data descriptors have a __set__ or a __delete__ attribute. Examples are
properties (defined in Python) and getsets and members (defined in C). properties (defined in Python) and getsets and members (defined in C).
Typically, data descriptors will also have __name__ and __doc__ attributes Typically, data descriptors will also have __name__ and __doc__ attributes
(properties, getsets, and members have both of these attributes), but this (properties, getsets, and members have both of these attributes), but this
...@@ -119,7 +119,7 @@ def isdatadescriptor(object): ...@@ -119,7 +119,7 @@ def isdatadescriptor(object):
# mutual exclusion # mutual exclusion
return False return False
tp = type(object) tp = type(object)
return hasattr(tp, "__set__") and hasattr(tp, "__get__") return hasattr(tp, "__set__") or hasattr(tp, "__delete__")
if hasattr(types, 'MemberDescriptorType'): if hasattr(types, 'MemberDescriptorType'):
# CPython and equivalent # CPython and equivalent
......
...@@ -1134,6 +1134,61 @@ class TestClassesAndFunctions(unittest.TestCase): ...@@ -1134,6 +1134,61 @@ class TestClassesAndFunctions(unittest.TestCase):
attrs = [a[0] for a in inspect.getmembers(C)] attrs = [a[0] for a in inspect.getmembers(C)]
self.assertNotIn('missing', attrs) self.assertNotIn('missing', attrs)
class TestIsDataDescriptor(unittest.TestCase):
def test_custom_descriptors(self):
class NonDataDescriptor:
def __get__(self, value, type=None): pass
class DataDescriptor0:
def __set__(self, name, value): pass
class DataDescriptor1:
def __delete__(self, name): pass
class DataDescriptor2:
__set__ = None
self.assertFalse(inspect.isdatadescriptor(NonDataDescriptor()),
'class with only __get__ not a data descriptor')
self.assertTrue(inspect.isdatadescriptor(DataDescriptor0()),
'class with __set__ is a data descriptor')
self.assertTrue(inspect.isdatadescriptor(DataDescriptor1()),
'class with __delete__ is a data descriptor')
self.assertTrue(inspect.isdatadescriptor(DataDescriptor2()),
'class with __set__ = None is a data descriptor')
def test_slot(self):
class Slotted:
__slots__ = 'foo',
self.assertTrue(inspect.isdatadescriptor(Slotted.foo),
'a slot is a data descriptor')
def test_property(self):
class Propertied:
@property
def a_property(self):
pass
self.assertTrue(inspect.isdatadescriptor(Propertied.a_property),
'a property is a data descriptor')
def test_functions(self):
class Test(object):
def instance_method(self): pass
@classmethod
def class_method(cls): pass
@staticmethod
def static_method(): pass
def function():
pass
a_lambda = lambda: None
self.assertFalse(inspect.isdatadescriptor(Test().instance_method),
'a instance method is not a data descriptor')
self.assertFalse(inspect.isdatadescriptor(Test().class_method),
'a class method is not a data descriptor')
self.assertFalse(inspect.isdatadescriptor(Test().static_method),
'a static method is not a data descriptor')
self.assertFalse(inspect.isdatadescriptor(function),
'a function is not a data descriptor')
self.assertFalse(inspect.isdatadescriptor(a_lambda),
'a lambda is not a data descriptor')
_global_ref = object() _global_ref = object()
class TestGetClosureVars(unittest.TestCase): class TestGetClosureVars(unittest.TestCase):
...@@ -3792,7 +3847,7 @@ def test_main(): ...@@ -3792,7 +3847,7 @@ def test_main():
TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState, TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject, TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject,
TestBoundArguments, TestSignaturePrivateHelpers, TestBoundArguments, TestSignaturePrivateHelpers,
TestSignatureDefinitions, TestSignatureDefinitions, TestIsDataDescriptor,
TestGetClosureVars, TestUnwrap, TestMain, TestReload, TestGetClosureVars, TestUnwrap, TestMain, TestReload,
TestGetCoroutineState TestGetCoroutineState
) )
......
...@@ -603,6 +603,7 @@ Peter Haight ...@@ -603,6 +603,7 @@ Peter Haight
Václav Haisman Václav Haisman
Zbigniew Halas Zbigniew Halas
Walker Hale IV Walker Hale IV
Aaron Christopher Hall
Bob Halley Bob Halley
Jesse Hallio Jesse Hallio
Jun Hamano Jun Hamano
......
Correct ``inspect.isdatadescriptor`` to look for ``__set__`` or
``__delete__``. Patch by Aaron Hall.
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