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

Serhiy Storchaka webhook-mailer at python.org
Sun May 20 19:46:45 EDT 2018


https://github.com/python/cpython/commit/4054b172ab59054715a2aaf4979bd84ac23e3ada
commit: 4054b172ab59054715a2aaf4979bd84ac23e3ada
branch: master
author: Aaron Hall, MBA <aaronchall at yahoo.com>
committer: Serhiy Storchaka <storchaka at gmail.com>
date: 2018-05-21T02:46:42+03:00
summary:

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

Look for '__set__' or '__delete__'.

files:
A Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst
M Doc/howto/descriptor.rst
M Lib/inspect.py
M Lib/test/test_inspect.py
M Misc/ACKS

diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst
index 5e85a9aa6594..6e4aa3e975f6 100644
--- a/Doc/howto/descriptor.rst
+++ b/Doc/howto/descriptor.rst
@@ -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
 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
 non-data descriptors (they are typically used for methods but other uses are
 possible).
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 512785f9237e..e5d312eb3021 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -110,7 +110,7 @@ def ismethoddescriptor(object):
 def isdatadescriptor(object):
     """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).
     Typically, data descriptors will also have __name__ and __doc__ attributes
     (properties, getsets, and members have both of these attributes), but this
@@ -119,7 +119,7 @@ def isdatadescriptor(object):
         # mutual exclusion
         return False
     tp = type(object)
-    return hasattr(tp, "__set__") and hasattr(tp, "__get__")
+    return hasattr(tp, "__set__") or hasattr(tp, "__delete__")
 
 if hasattr(types, 'MemberDescriptorType'):
     # CPython and equivalent
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 3481a57ec833..ee227a66b17c 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -1134,6 +1134,61 @@ class C(metaclass=M):
         attrs = [a[0] for a in inspect.getmembers(C)]
         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()
 class TestGetClosureVars(unittest.TestCase):
@@ -3792,7 +3847,7 @@ def test_main():
         TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
         TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject,
         TestBoundArguments, TestSignaturePrivateHelpers,
-        TestSignatureDefinitions,
+        TestSignatureDefinitions, TestIsDataDescriptor,
         TestGetClosureVars, TestUnwrap, TestMain, TestReload,
         TestGetCoroutineState
     )
diff --git a/Misc/ACKS b/Misc/ACKS
index cb7f4cd0275b..4d295b60a065 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -603,6 +603,7 @@ Peter Haight
 Václav Haisman
 Zbigniew Halas
 Walker Hale IV
+Aaron Christopher Hall
 Bob Halley
 Jesse Hallio
 Jun Hamano
diff --git a/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst b/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst
new file mode 100644
index 000000000000..cb4c41ba5db9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst
@@ -0,0 +1,2 @@
+Correct ``inspect.isdatadescriptor`` to look for ``__set__`` or
+``__delete__``.  Patch by Aaron Hall.



More information about the Python-checkins mailing list