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.

classification
Title: Add __qualname__ for attributes of Mock instances
Type: enhancement Stage:
Components: Tests Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: michael.foord, s_kostyuk, xtreak
Priority: normal Keywords:

Created on 2018-02-25 19:52 by s_kostyuk, last changed 2022-04-11 14:58 by admin.

Files
File name Uploaded Description Edit
qualname_for_mocks.py s_kostyuk, 2018-02-25 19:52 This file contains an illustation of the current and desired behaviour of Mocks from unittest.mock Python module
Messages (2)
msg312849 - (view) Author: Sergey Kostyuk (s_kostyuk) Date: 2018-02-25 19:52
Good day. I have a question (or proposal, if you like)

For now Mocks from unittest.mock module allows to mimic an interface of a some class or object instance. They pass isinstance checks, they allows to wrap callables with respect to their arguments. But there is a thing they don't mimic: a value of the __qualname__ attribute for a mock itself and its mocked attributes.

So, here is the proposal: copy the value of __qualname__ attribute from the wrapped (mocked) instance for all of the attributes of a Mock.

I don't know if it's reasonable enough to be implemented at all but it can be handy in some situations.

An example of the current and desired behaviour is provided in the attached file. And sorry for my English
msg358327 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-12-13 11:03
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
2022-04-11 14:58:58adminsetgithub: 77133
2019-12-13 11:03:26xtreaksetmessages: + msg358327
2019-12-13 10:38:45serhiy.storchakasetnosy: + michael.foord

versions: + Python 3.9, - Python 3.8
2019-12-13 09:58:10xtreaksetnosy: + xtreak
2018-02-25 19:52:11s_kostyukcreate