]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-102978: Fix mock.patch function signatures for class and staticmethod decorators...
authorTomas R <tomas.roun8@gmail.com>
Thu, 13 Apr 2023 07:37:57 +0000 (09:37 +0200)
committerGitHub <noreply@github.com>
Thu, 13 Apr 2023 07:37:57 +0000 (08:37 +0100)
Fixes unittest.mock.patch not enforcing function signatures for methods
decorated with @classmethod or @staticmethod when patch is called with
autospec=True.

Lib/test/test_unittest/testmock/testhelpers.py
Lib/test/test_unittest/testmock/testpatch.py
Lib/unittest/mock.py
Misc/ACKS
Misc/NEWS.d/next/Library/2023-04-03-23-44-34.gh-issue-102978.gy9eVk.rst [new file with mode: 0644]

index dc4d004cda8a75872c3c9adbf5d17834b5ed74ff..74785a83757a92d7c1e77e660c570b74b2bcbe72 100644 (file)
@@ -952,6 +952,24 @@ class SpecSignatureTest(unittest.TestCase):
         self.assertFalse(hasattr(autospec, '__name__'))
 
 
+    def test_autospec_signature_staticmethod(self):
+        class Foo:
+            @staticmethod
+            def static_method(a, b=10, *, c): pass
+
+        mock = create_autospec(Foo.__dict__['static_method'])
+        self.assertEqual(inspect.signature(Foo.static_method), inspect.signature(mock))
+
+
+    def test_autospec_signature_classmethod(self):
+        class Foo:
+            @classmethod
+            def class_method(cls, a, b=10, *, c): pass
+
+        mock = create_autospec(Foo.__dict__['class_method'])
+        self.assertEqual(inspect.signature(Foo.class_method), inspect.signature(mock))
+
+
     def test_spec_inspect_signature(self):
 
         def myfunc(x, y): pass
index 8ceb5d973e1aafb702b1db680069697d3aaaf9c3..833d7da1f31a20677b17118de3764f68e5cb0c75 100644 (file)
@@ -996,6 +996,36 @@ class PatchTest(unittest.TestCase):
             method.assert_called_once_with()
 
 
+    def test_autospec_staticmethod_signature(self):
+        # Patched methods which are decorated with @staticmethod should have the same signature
+        class Foo:
+            @staticmethod
+            def static_method(a, b=10, *, c): pass
+
+        Foo.static_method(1, 2, c=3)
+
+        with patch.object(Foo, 'static_method', autospec=True) as method:
+            method(1, 2, c=3)
+            self.assertRaises(TypeError, method)
+            self.assertRaises(TypeError, method, 1)
+            self.assertRaises(TypeError, method, 1, 2, 3, c=4)
+
+
+    def test_autospec_classmethod_signature(self):
+        # Patched methods which are decorated with @classmethod should have the same signature
+        class Foo:
+            @classmethod
+            def class_method(cls, a, b=10, *, c): pass
+
+        Foo.class_method(1, 2, c=3)
+
+        with patch.object(Foo, 'class_method', autospec=True) as method:
+            method(1, 2, c=3)
+            self.assertRaises(TypeError, method)
+            self.assertRaises(TypeError, method, 1)
+            self.assertRaises(TypeError, method, 1, 2, 3, c=4)
+
+
     def test_autospec_with_new(self):
         patcher = patch('%s.function' % __name__, new=3, autospec=True)
         self.assertRaises(TypeError, patcher.start)
index 0f93cb53c3d5cea6f6a3d065ca4e18c453a1e688..7ca085760650afad5b5fe2ca9b8e890affd50ed3 100644 (file)
@@ -98,6 +98,12 @@ def _get_signature_object(func, as_instance, eat_self):
         func = func.__init__
         # Skip the `self` argument in __init__
         eat_self = True
+    elif isinstance(func, (classmethod, staticmethod)):
+        if isinstance(func, classmethod):
+            # Skip the `cls` argument of a class method
+            eat_self = True
+        # Use the original decorated method to extract the correct function signature
+        func = func.__func__
     elif not isinstance(func, FunctionTypes):
         # If we really want to model an instance of the passed type,
         # __call__ should be looked up, not __init__.
index 1e94d33a665e4c6b669f3cf19a012fb7f911475c..d0ff4e8aeb5c90364bf42e1b228d054100296c16 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1550,6 +1550,7 @@ Hugo van Rossum
 Saskia van Rossum
 Robin Roth
 Clement Rouault
+Tomas Roun
 Donald Wallace Rouse II
 Liam Routt
 Todd Rovito
diff --git a/Misc/NEWS.d/next/Library/2023-04-03-23-44-34.gh-issue-102978.gy9eVk.rst b/Misc/NEWS.d/next/Library/2023-04-03-23-44-34.gh-issue-102978.gy9eVk.rst
new file mode 100644 (file)
index 0000000..df63af1
--- /dev/null
@@ -0,0 +1,3 @@
+Fixes :func:`unittest.mock.patch` not enforcing function signatures for methods
+decorated with ``@classmethod`` or ``@staticmethod`` when patch is called with
+``autospec=True``.