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()