changeset: 102559:e947248100ae branch: 3.5 parent: 102557:cbcab1fb1b8a user: Gregory P. Smith date: Sun Aug 07 08:52:26 2016 -0700 files: Lib/unittest/mock.py Lib/unittest/test/testmock/testhelpers.py Misc/NEWS description: Issue #26750: unittest.mock.create_autospec() now works properly for subclasses of property() and other data descriptors. diff -r cbcab1fb1b8a -r e947248100ae Lib/unittest/mock.py --- a/Lib/unittest/mock.py Sat Aug 06 13:46:48 2016 -0700 +++ b/Lib/unittest/mock.py Sun Aug 07 08:52:26 2016 -0700 @@ -64,12 +64,20 @@ __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 @@ _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 = {} diff -r cbcab1fb1b8a -r e947248100ae Lib/unittest/test/testmock/testhelpers.py --- a/Lib/unittest/test/testmock/testhelpers.py Sat Aug 06 13:46:48 2016 -0700 +++ b/Lib/unittest/test/testmock/testhelpers.py Sun Aug 07 08:52:26 2016 -0700 @@ -802,35 +802,53 @@ a.f.assert_called_with(self=10) - def test_autospec_property(self): + def test_autospec_data_descriptor(self): + class Descriptor(object): + def __init__(self, value): + self.value = value + + def __get__(self, obj, cls=None): + if obj is None: + return self + return self.value + + def __set__(self, obj, value): + pass + + class MyProperty(property): + pass + class Foo(object): + __slots__ = ['slot'] + @property - def foo(self): + def prop(self): return 3 + @MyProperty + def subprop(self): + return 4 + + desc = Descriptor(42) + foo = create_autospec(Foo) - mock_property = foo.foo - # 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 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) - def test_autospec_slots(self): - class Foo(object): - __slots__ = ['a'] - - 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) + # 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): diff -r cbcab1fb1b8a -r e947248100ae Misc/NEWS --- a/Misc/NEWS Sat Aug 06 13:46:48 2016 -0700 +++ b/Misc/NEWS Sun Aug 07 08:52:26 2016 -0700 @@ -34,6 +34,9 @@ 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.