Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a1b5a6b

Browse files
committed
Fix mock.patch function signatures for class and staticmethod decorators
Fixes unittest.mock.patch not enforcing function signatures for methods decorated with @classmethod or @staticmethod when patch is called with autospec=True.
1 parent fd1947e commit a1b5a6b

File tree

5 files changed

+62
-0
lines changed

5 files changed

+62
-0
lines changed

Lib/test/test_unittest/testmock/testhelpers.py

+20
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,26 @@ def __getattr__(self, attribute):
952952
self.assertFalse(hasattr(autospec, '__name__'))
953953

954954

955+
def test_autospec_signature_staticmethod(self):
956+
# Regression test for gh-102978
957+
class Foo:
958+
@staticmethod
959+
def static_method(a, b=10, *, c): pass
960+
961+
mock = create_autospec(Foo.__dict__['static_method'])
962+
self.assertEqual(inspect.signature(Foo.static_method), inspect.signature(mock))
963+
964+
965+
def test_autospec_signature_classmethod(self):
966+
# Regression test for gh-102978
967+
class Foo:
968+
@classmethod
969+
def class_method(cls, a, b=10, *, c): pass
970+
971+
mock = create_autospec(Foo.__dict__['class_method'])
972+
self.assertEqual(inspect.signature(Foo.class_method), inspect.signature(mock))
973+
974+
955975
def test_spec_inspect_signature(self):
956976

957977
def myfunc(x, y): pass

Lib/test/test_unittest/testmock/testpatch.py

+32
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,38 @@ def test_autospec_classmethod(self):
996996
method.assert_called_once_with()
997997

998998

999+
def test_autospec_staticmethod_signature(self):
1000+
# Regression test for gh-102978
1001+
# Patched methods which are decorated with @staticmethod should have the same signature
1002+
class Foo:
1003+
@staticmethod
1004+
def static_method(a, b=10, *, c): pass
1005+
1006+
Foo.static_method(1, 2, c=3)
1007+
1008+
with patch.object(Foo, 'static_method', autospec=True) as method:
1009+
method(1, 2, c=3)
1010+
self.assertRaises(TypeError, method)
1011+
self.assertRaises(TypeError, method, 1)
1012+
self.assertRaises(TypeError, method, 1, 2, 3, c=4)
1013+
1014+
1015+
def test_autospec_classmethod_signature(self):
1016+
# Regression test for gh-102978
1017+
# Patched methods which are decorated with @classmethod should have the same signature
1018+
class Foo:
1019+
@classmethod
1020+
def class_method(cls, a, b=10, *, c): pass
1021+
1022+
Foo.class_method(1, 2, c=3)
1023+
1024+
with patch.object(Foo, 'class_method', autospec=True) as method:
1025+
method(1, 2, c=3)
1026+
self.assertRaises(TypeError, method)
1027+
self.assertRaises(TypeError, method, 1)
1028+
self.assertRaises(TypeError, method, 1, 2, 3, c=4)
1029+
1030+
9991031
def test_autospec_with_new(self):
10001032
patcher = patch('%s.function' % __name__, new=3, autospec=True)
10011033
self.assertRaises(TypeError, patcher.start)

Lib/unittest/mock.py

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def _get_signature_object(func, as_instance, eat_self):
9898
func = func.__init__
9999
# Skip the `self` argument in __init__
100100
eat_self = True
101+
elif isinstance(func, (classmethod, staticmethod)):
102+
if isinstance(func, classmethod):
103+
# Skip the `cls` argument of a class method
104+
eat_self = True
105+
# Use the original decorated method to extract the correct function signature
106+
func = func.__func__
101107
elif not isinstance(func, FunctionTypes):
102108
# If we really want to model an instance of the passed type,
103109
# __call__ should be looked up, not __init__.

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ Hugo van Rossum
15471547
Saskia van Rossum
15481548
Robin Roth
15491549
Clement Rouault
1550+
Tomas Roun
15501551
Donald Wallace Rouse II
15511552
Liam Routt
15521553
Todd Rovito
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixes :func:`unittest.mock.patch` not enforcing function signatures for methods
2+
decorated with ``@classmethod`` or ``@staticmethod`` when patch is called with
3+
``autospec=True``.

0 commit comments

Comments
 (0)