Issue #22138: Fix mock.patch behavior when patching descriptors. Restore original values after patching. Patch contributed by Sean McCully.
diff --git a/NEWS b/NEWS index 5e91ac8..2e13b8a 100644 --- a/NEWS +++ b/NEWS
@@ -1,6 +1,9 @@ Library ------- +- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore + original values after patching. Patch contributed by Sean McCully. + - Issue #24857: Comparing call_args to a long sequence now correctly returns a boolean result instead of raising an exception. Patch by A Kaptur.
diff --git a/mock/mock.py b/mock/mock.py index 4846a46..8174766 100644 --- a/mock/mock.py +++ b/mock/mock.py
@@ -1477,7 +1477,10 @@ setattr(self.target, self.attribute, self.temp_original) else: delattr(self.target, self.attribute) - if not self.create and not hasattr(self.target, self.attribute): + if not self.create and (not hasattr(self.target, self.attribute) or + self.attribute in ('__doc__', '__module__', + '__defaults__', '__annotations__', + '__kwdefaults__')): # needed for proxy objects like django settings setattr(self.target, self.attribute, self.temp_original)
diff --git a/mock/tests/testpatch.py b/mock/tests/testpatch.py index 761ce99..32a6c27 100644 --- a/mock/tests/testpatch.py +++ b/mock/tests/testpatch.py
@@ -1852,5 +1852,32 @@ self.assertEqual(stopped, ["three", "two", "one"]) + def test_special_attrs(self): + def foo(x=0): + """TEST""" + return x + with patch.object(foo, '__defaults__', (1, )): + self.assertEqual(foo(), 1) + self.assertEqual(foo(), 0) + + with patch.object(foo, '__doc__', "FUN"): + self.assertEqual(foo.__doc__, "FUN") + self.assertEqual(foo.__doc__, "TEST") + + with patch.object(foo, '__module__', "testpatch2"): + self.assertEqual(foo.__module__, "testpatch2") + self.assertEqual(foo.__module__, __name__) + + if hasattr(self.test_special_attrs, '__annotations__'): + with patch.object(foo, '__annotations__', dict([('s', 1, )])): + self.assertEqual(foo.__annotations__, dict([('s', 1, )])) + self.assertEqual(foo.__annotations__, dict()) + + if hasattr(self.test_special_attrs, '__kwdefaults__'): + foo = eval("lambda *a, x=0: x") + with patch.object(foo, '__kwdefaults__', dict([('x', 1, )])): + self.assertEqual(foo(), 1) + self.assertEqual(foo(), 0) + if __name__ == '__main__': unittest.main()