]> granicus.if.org Git - python/commitdiff
Issue #26750: unittest.mock.create_autospec() now works properly
authorGregory P. Smith <greg@krypto.org>
Sun, 7 Aug 2016 15:52:26 +0000 (08:52 -0700)
committerGregory P. Smith <greg@krypto.org>
Sun, 7 Aug 2016 15:52:26 +0000 (08:52 -0700)
for subclasses of property() and other data descriptors.

Lib/unittest/mock.py
Lib/unittest/test/testmock/testhelpers.py
Misc/NEWS

index 7f30b287ae057479a74052f886827879d4bc3cfc..83e9c46676e8b685631e1e4e9797aadfdb721f5e 100644 (file)
@@ -64,12 +64,20 @@ class _slotted(object):
     __slots__ = ['a']
 
 
+# Do not use this tuple.  It was never documented as a public API.
+# It will be removed.  It has no obvious signs of users on github.
 DescriptorTypes = (
     type(_slotted.a),
     property,
 )
 
 
+def _is_data_descriptor(obj):
+    # Data descriptors are Properties, slots, getsets and C data members.
+    return ((hasattr(obj, '__set__') or hasattr(obj, '__del__')) and
+            hasattr(obj, '__get__'))
+
+
 def _get_signature_object(func, as_instance, eat_self):
     """
     Given an arbitrary, possibly callable object, try to create a suitable
@@ -2130,7 +2138,7 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
     _kwargs.update(kwargs)
 
     Klass = MagicMock
-    if type(spec) in DescriptorTypes:
+    if _is_data_descriptor(spec):
         # descriptors don't have a spec
         # because we don't know what type they return
         _kwargs = {}
index 1dbc0b64ba58bfea9deff98dfdd7ea2da10079b8..34776347daa637419cdf66797ba76269565b498b 100644 (file)
@@ -802,35 +802,53 @@ class SpecSignatureTest(unittest.TestCase):
         a.f.assert_called_with(self=10)
 
 
-    def test_autospec_property(self):
-        class Foo(object):
-            @property
-            def foo(self):
-                return 3
+    def test_autospec_data_descriptor(self):
+        class Descriptor(object):
+            def __init__(self, value):
+                self.value = value
 
-        foo = create_autospec(Foo)
-        mock_property = foo.foo
+            def __get__(self, obj, cls=None):
+                if obj is None:
+                    return self
+                return self.value
 
-        # no spec on properties
-        self.assertIsInstance(mock_property, MagicMock)
-        mock_property(1, 2, 3)
-        mock_property.abc(4, 5, 6)
-        mock_property.assert_called_once_with(1, 2, 3)
-        mock_property.abc.assert_called_once_with(4, 5, 6)
+            def __set__(self, obj, value):
+                pass
 
+        class MyProperty(property):
+            pass
 
-    def test_autospec_slots(self):
         class Foo(object):
-            __slots__ = ['a']
+            __slots__ = ['slot']
+
+            @property
+            def prop(self):
+                return 3
+
+            @MyProperty
+            def subprop(self):
+                return 4
+
+            desc = Descriptor(42)
 
         foo = create_autospec(Foo)
-        mock_slot = foo.a
 
-        # no spec on slots
-        mock_slot(1, 2, 3)
-        mock_slot.abc(4, 5, 6)
-        mock_slot.assert_called_once_with(1, 2, 3)
-        mock_slot.abc.assert_called_once_with(4, 5, 6)
+        def check_data_descriptor(mock_attr):
+            # Data descriptors don't have a spec.
+            self.assertIsInstance(mock_attr, MagicMock)
+            mock_attr(1, 2, 3)
+            mock_attr.abc(4, 5, 6)
+            mock_attr.assert_called_once_with(1, 2, 3)
+            mock_attr.abc.assert_called_once_with(4, 5, 6)
+
+        # property
+        check_data_descriptor(foo.prop)
+        # property subclass
+        check_data_descriptor(foo.subprop)
+        # class __slot__
+        check_data_descriptor(foo.slot)
+        # plain data descriptor
+        check_data_descriptor(foo.desc)
 
 
 class TestCallList(unittest.TestCase):
index cdbb1a4d6aa65225dce4869445c819daa82822c9..e6548c283abb99907d30a731d202b88ef32c1b65 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -34,6 +34,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #26750: unittest.mock.create_autospec() now works properly for
+  subclasses of property() and other data descriptors.
+
 - Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
   HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
   that the script is in CGI mode.