New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mock.create_autospec fails if an attribute is a partial function #76334
Comments
If an object's attribute is a partial function, mock.create_autospec will fail while trying to copy the partial functions' details to the mocked function, as the partial function does not have the __name__ attribute. Example: import functools
from unittest import mock
class Proxy(object):
def __init__(self, obj):
self.obj
def __getattr__(self, name):
return functools.partial(self.__run_method, name)
def __run_method(self, name, *args, **kwargs):
return getattr(self.obj, name)(*args, **kwargs)
a = mock.Mock()
proxy = Proxy(a)
mock.create_autospec(proxy) Output: Traceback (most recent call last):
File "py3_mock_functools.py", line 20, in <module>
mock.create_autospec(proxy)
File "/usr/lib/python3.5/unittest/mock.py", line 2156, in create_autospec
_check_signature(spec, mock, is_type, instance)
File "/usr/lib/python3.5/unittest/mock.py", line 112, in _check_signature
_copy_func_details(func, checksig)
File "/usr/lib/python3.5/unittest/mock.py", line 117, in _copy_func_details
funcopy.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__' |
It seems to me that we have three alternatives :
It seems obvious to me that option 3 is correct - just a question of what funcopy dunder_name should be set to. I would imagine there is code that uses funcopy dunder_name in some way ? |
I think option 2 is what functools.update_wrapper() does and it may be better to make create_autospec() consistent with it. Perhaps we can replace _copy_func_details() with functools.update_wrapper(): assigments = (
'__name__', '__doc__', '__text_signature__',
# And the rest of the attributes in _copy_func_details().
)
functools.update_wrapper(..., assigned=assignments, updated=()) |
Or we can special case functools.partial() in _copy_func_details() and use partial_object.func.__name__. (I'm not sure which solution is better, but we need to the same thing for __doc__ as well.) |
Am not a big fan of special casing, I think the functools.update_wrapper is the way to go - will have a look later and produce a pull request with some test cases. |
I think the original error has been fixed with bpo-28919 where the attribute errors are ignored while copying the functions as suggested by Anthony in solution 2. So can this issue be closed as outdated to reopen a new one for using update_wrapper as enhancement or the title can be changed to reflect the fact that autospec should now use update_wrapper instead of using _copy_func_details ? Correct me if I am wrong on the workflow to update the ticket. Also there doesn't seem to be any test for this that can possibly added. Current implementation : def _copy_func_details(func, funcopy):
# we explicitly don't copy func.__dict__ into this copy as it would
# expose original attributes that should be mocked
for attribute in (
'__name__', '__doc__', '__text_signature__',
'__module__', '__defaults__', '__kwdefaults__',
):
try:
setattr(funcopy, attribute, getattr(func, attribute))
except AttributeError:
pass Thanks |
Yes, it would be great if we could convert the snippet in msg307115 to a test case before closing this issue. 3.6 still has this bug, but since we are pretty close to the end of its bugfix maintenance period, we can skip it. |
@berker.peksag I have created a unit test PR and verified that the test fails in 3.6 and passes on master. # 3.6 branch cpython git:(25bd107399) ./python.exe
Python 3.6.7+ (remotes/upstream/3.6:25bd107399, Nov 8 2018, 00:50:43)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
cpython git:(25bd107399) ./python.exe -m unittest -v unittest.test.testmock.testhelpers.SpecSignatureTest.test_autospec_getattr_partial_function
test_autospec_getattr_partial_function (unittest.test.testmock.testhelpers.SpecSignatureTest) ... ERROR ====================================================================== Traceback (most recent call last):
File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/test/testmock/testhelpers.py", line 884, in test_autospec_getattr_partial_function
autospec = create_autospec(proxy)
File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 2182, in create_autospec
_check_signature(spec, mock, is_type, instance)
File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 102, in _check_signature
_copy_func_details(func, checksig)
File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 107, in _copy_func_details
funcopy.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__' Ran 1 test in 0.007s FAILED (errors=1) # Master cpython git:(bpo32153) ./python.exe
Python 3.8.0a0 (heads/bpo32153:3e9cd8d982, Nov 8 2018, 00:53:46)
[Clang 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D
cpython git:(bpo32153) ./python.exe -m unittest -v unittest.test.testmock.testhelpers.SpecSignatureTest.test_autospec_getattr_partial_function
test_autospec_getattr_partial_function (unittest.test.testmock.testhelpers.SpecSignatureTest) ... ok Ran 1 test in 0.005s OK |
@cjw296 since the unit tests were added and the original report is fixed with 3.7 and above can this be closed? |
Yep! Good catch :-) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: