mirror of
https://github.com/python/cpython.git
synced 2025-10-03 13:45:29 +00:00
gh-102978: Fix mock.patch function signatures for class and staticmethod decorators (#103228)
Fixes unittest.mock.patch not enforcing function signatures for methods decorated with @classmethod or @staticmethod when patch is called with autospec=True.
This commit is contained in:
parent
19d2639d1e
commit
59e0de4903
5 changed files with 58 additions and 0 deletions
|
@ -952,6 +952,24 @@ class SpecSignatureTest(unittest.TestCase):
|
||||||
self.assertFalse(hasattr(autospec, '__name__'))
|
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 test_spec_inspect_signature(self):
|
||||||
|
|
||||||
def myfunc(x, y): pass
|
def myfunc(x, y): pass
|
||||||
|
|
|
@ -996,6 +996,36 @@ class PatchTest(unittest.TestCase):
|
||||||
method.assert_called_once_with()
|
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):
|
def test_autospec_with_new(self):
|
||||||
patcher = patch('%s.function' % __name__, new=3, autospec=True)
|
patcher = patch('%s.function' % __name__, new=3, autospec=True)
|
||||||
self.assertRaises(TypeError, patcher.start)
|
self.assertRaises(TypeError, patcher.start)
|
||||||
|
|
|
@ -98,6 +98,12 @@ def _get_signature_object(func, as_instance, eat_self):
|
||||||
func = func.__init__
|
func = func.__init__
|
||||||
# Skip the `self` argument in __init__
|
# Skip the `self` argument in __init__
|
||||||
eat_self = True
|
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):
|
elif not isinstance(func, FunctionTypes):
|
||||||
# If we really want to model an instance of the passed type,
|
# If we really want to model an instance of the passed type,
|
||||||
# __call__ should be looked up, not __init__.
|
# __call__ should be looked up, not __init__.
|
||||||
|
|
|
@ -1550,6 +1550,7 @@ Hugo van Rossum
|
||||||
Saskia van Rossum
|
Saskia van Rossum
|
||||||
Robin Roth
|
Robin Roth
|
||||||
Clement Rouault
|
Clement Rouault
|
||||||
|
Tomas Roun
|
||||||
Donald Wallace Rouse II
|
Donald Wallace Rouse II
|
||||||
Liam Routt
|
Liam Routt
|
||||||
Todd Rovito
|
Todd Rovito
|
||||||
|
|
|
@ -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``.
|
Loading…
Add table
Add a link
Reference in a new issue