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.patch.object does not persist __module__ name for functions #81015
Comments
The expectation is that the __module__ attribute for a patched function should persist after patching. Minimal test case is attached. Simply run pytest in a venv with the files. Output:
E AssertionError: assert None == 'mymodule' test_mymodule.py:8: AssertionError Originally reported at pytest-dev/pytest-mock#146 before it was determined this was a unittest.mock issue. Happens on both Python 2.7 and 3.7. Probably not really tied to a specific Python version and more of mock library issue. My local venv: |
I guess the problem is that to patch 'bar' in foo.bar the signature of bar is taken and then it's evaluated using exec [1] where the function body has _check_sig that checks for the signature during function call. _check_sig has attributes of func copied. Since it's evaluated using exec the returned funcopy variable might not have __module__ set as per the execution context. One possible approach would be to _copy_func_details(func, funcopy) so that the __module__ and other attributes are copied. This produces a test failure where in case of lambdas the __name__ is <lambda> which is not a valid identifier and hence funcopy is used. Then __name__ is set as '<lambda>' by my patch. Hence, 'funcopy' is replaced by '<lambda>' causing below test failure. This could be resolved by setting __name__ again to the expected value. This was newly added for test coverage with adbf178. My main worry is that it's used in create_autospec hence do we really want to copy __module__ and others to the patched function for places with create_autospec also with functions supplying autospec=True. This is a user expectation but I would like to know maintainers opinion as well of this change. Reproducer # cat /tmp/foo.py def bar(a: int):
pass
# /tmp/bar.py
from unittest import mock
import foo
with mock.patch.object(foo, 'bar', autospec=True) as mocked_attribute:
assert mocked_attribute.__module__ == 'foo' # Python 3.7 __module__ is None
➜ cpython git:(master) ✗ python3.7 /tmp/bar.py
Traceback (most recent call last):
File "/tmp/bar.py", line 6, in <module>
assert mocked_attribute.__module__ == 'foo', f'__module__ is {mocked_attribute.__module__}'
AssertionError: __module__ is None # With patch ➜ cpython git:(master) ✗ ./python.exe /tmp/bar.py # No failure # Patch diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 1e8057d5f5..a7006fcf7d 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -165,6 +165,8 @@ def _set_signature(mock, original, instance=False):
exec (src, context)
funcopy = context[name]
_setup_func(funcopy, mock, sig)
+ _copy_func_details(func, funcopy)
+ funcopy.__name__ = name
return funcopy # Test failure without funcopy.__name__ = name in my patch where I don't restore the 'funcopy' for lambdas. But this can be fixed. ====================================================================== Traceback (most recent call last):
File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/test/testmock/testhelpers.py", line 946, in test_spec_function_no_name
self.assertEqual(mock.__name__, 'funcopy')
AssertionError: '<lambda>' != 'funcopy'
- <lambda>
+ funcopy Ran 386 tests in 2.911s [1] Line 165 in e85ef7a
|
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: