Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e95ca78

Browse filesBrowse files
[3.11] gh-102978: Fix mock.patch function signatures for class and staticmethod decorators (GH-103228) (#103499)
Fixes unittest.mock.patch not enforcing function signatures for methods decorated with @classmethod or @staticmethod when patch is called with autospec=True. (cherry picked from commit 59e0de4) Co-authored-by: Tomas R <tomas.roun8@gmail.com>
1 parent 1692a16 commit e95ca78
Copy full SHA for e95ca78

File tree

Expand file treeCollapse file tree

5 files changed

+58
-0
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+58
-0
lines changed

‎Lib/unittest/mock.py

Copy file name to clipboardExpand all lines: Lib/unittest/mock.py
+6Lines changed: 6 additions & 0 deletions
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__.

‎Lib/unittest/test/testmock/testhelpers.py

Copy file name to clipboardExpand all lines: Lib/unittest/test/testmock/testhelpers.py
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,24 @@ def __getattr__(self, attribute):
952952
self.assertFalse(hasattr(autospec, '__name__'))
953953

954954

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

957975
def myfunc(x, y): pass

‎Lib/unittest/test/testmock/testpatch.py

Copy file name to clipboardExpand all lines: Lib/unittest/test/testmock/testpatch.py
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,36 @@ def test_autospec_classmethod(self):
996996
method.assert_called_once_with()
997997

998998

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

‎Misc/ACKS

Copy file name to clipboardExpand all lines: Misc/ACKS
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,7 @@ Hugo van Rossum
15331533
Saskia van Rossum
15341534
Robin Roth
15351535
Clement Rouault
1536+
Tomas Roun
15361537
Donald Wallace Rouse II
15371538
Liam Routt
15381539
Todd Rovito
+3Lines changed: 3 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.