Karthikeyan Singaravelan <[email protected]> added the comment:
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
adbf178e49113b2de0042e86a1228560475a65c5.
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.
======================================================================
FAIL: test_spec_function_no_name
(unittest.test.testmock.testhelpers.SpecSignatureTest)
----------------------------------------------------------------------
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]
https://github.com/python/cpython/blob/e85ef7a7eacdef2f43e6bf2e67f335100e7ef2da/Lib/unittest/mock.py#L165
----------
nosy: +cjw296, mariocj89, michael.foord, xtreak
type: -> behavior
versions: +Python 3.8 -Python 2.7
_______________________________________
Python tracker <[email protected]>
<https://bugs.python.org/issue36834>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com