This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author xtreak
Recipients michael.foord, s_kostyuk, xtreak
Date 2019-12-13.11:03:26
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1576235006.99.0.131656946084.issue32952@roundup.psfhosted.org>
In-reply-to
Content
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
History
Date User Action Args
2019-12-13 11:03:27xtreaksetrecipients: + xtreak, michael.foord, s_kostyuk
2019-12-13 11:03:26xtreaksetmessageid: <1576235006.99.0.131656946084.issue32952@roundup.psfhosted.org>
2019-12-13 11:03:26xtreaklinkissue32952 messages
2019-12-13 11:03:26xtreakcreate