create_autospec already does recursive spec setting on the class and it has the attributes/methods mocked to retain attributes but doesn't copy the metadata of the original methods to the mock itself. I am not sure spec_set needs to do more stuff than simple attribute level checks making it more heavy to resemble autospeccing and there is an issue for that already. Currently we don't copy __qualname__ to the mock at [0].
The func metadata are copied to checksig and during __call__ checksig is used but it's not usable outside and I am not sure we need to do it. The fix to do _copy_func_details(func, mock) below that would copy the function attributes to the actual mock thus accessing the attributes for the mock making below example work.
from unittest.mock import MagicMock, create_autospec, patch
class SomeClass(object):
def some_method(self):
print("Hi!")
if __name__ == "__main__":
ins = SomeClass()
assert ins.some_method.__qualname__ == "SomeClass.some_method"
with patch('__main__.SomeClass', autospec=True) as mocked:
obj = SomeClass()
assert obj.some_method.__qualname__ == "SomeClass.some_method"
obj.some_method()
obj.some_method.assert_called_once()
Copy metadata patch :
diff --git Lib/unittest/mock.py Lib/unittest/mock.py
index cd5a2aeb60..ea1c6c84ac 100644
--- Lib/unittest/mock.py
+++ Lib/unittest/mock.py
@@ -117,6 +117,7 @@ def _check_signature(func, mock, skipfirst, instance=False):
def checksig(self, /, *args, **kwargs):
sig.bind(*args, **kwargs)
_copy_func_details(func, checksig)
+ _copy_func_details(func, mock)
type(mock)._mock_check_sig = checksig
type(mock).__signature__ = sig
@@ -127,6 +128,7 @@ def _copy_func_details(func, funcopy):
for attribute in (
'__name__', '__doc__', '__text_signature__',
'__module__', '__defaults__', '__kwdefaults__',
+ '__qualname__'
):
try:
setattr(funcopy, attribute, getattr(func, attribute))
[0] https://github.com/python/cpython/blob/025a602af7ee284d8db6955c26016f3f27d35536/Lib/unittest/mock.py#L124
[1] https://github.com/python/cpython/blob/025a602af7ee284d8db6955c26016f3f27d35536/Lib/unittest/mock.py#L119
|