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
AsyncMock is unable to correctly patch static or class methods #83263
Comments
Currently, patch is unable to correctly patch coroutinefunctions decorated with Example:
This should ideally return an |
Using patch as a function just returns a patch object. You need to call start method on the patch object to return a Mock. |
There seems to be a difference in obtaining the attribute out of the __dict__ and getattr. patch uses __dict__ [0] to access the attribute to be patched and falls back to getattr. There is a difference in detecting normal attribute access to be a coroutine function versus the one obtained from __dict__ . We can perhaps make _is_async_obj [1] to see if the passed obj is a classmethod/staticmethod and to use __func__ to detect a coroutine function. As a workaround for now you can pass AsyncMock explicitly to patch to return an AsyncMock. There was an open issue (bpo-36092) about patch and staticmethods/classmethods but not sure this is related to this. Retagging it to remove asyncio. Thanks for the report! # bpo-39082.py from unittest.mock import patch
import inspect
class Helper:
print("Patching async static method")
async_patcher = patch("__main__.Helper.async_class_method")
print(f"{Helper.async_class_method = }")
print(f"{Helper.__dict__['async_class_method'] = }")
print(f"{inspect.iscoroutinefunction(Helper.async_class_method) = }")
print(f"{inspect.iscoroutinefunction(Helper.__dict__['async_class_method']) = }")
print(f"{inspect.iscoroutinefunction(getattr(Helper, 'async_class_method')) = }")
mock_ = async_patcher.start()
print(mock_)
print("\nPatching async static method")
async_patcher = patch("__main__.Helper.async_static_method")
print(f"{Helper.async_static_method = }")
print(f"{Helper.__dict__['async_static_method'] = }")
print(f"{inspect.iscoroutinefunction(Helper.async_static_method) = }")
print(f"{inspect.iscoroutinefunction(Helper.__dict__['async_static_method']) = }")
print(f"{inspect.iscoroutinefunction(getattr(Helper, 'async_static_method')) = }")
mock_ = async_patcher.start()
print(mock_) $ python3.8 bpo39082.py
Patching async static method
Helper.async_class_method = <bound method Helper.async_class_method of <class '__main__.Helper'>>
Helper.__dict__['async_class_method'] = <classmethod object at 0x10de7bcd0>
inspect.iscoroutinefunction(Helper.async_class_method) = True
inspect.iscoroutinefunction(Helper.__dict__['async_class_method']) = False
inspect.iscoroutinefunction(getattr(Helper, 'async_class_method')) = True
<MagicMock name='async_class_method' id='4537489440'> Patching async static method Detect __func__ to be used for iscoroutinefunction diff --git Lib/unittest/mock.py Lib/unittest/mock.py
index cd5a2aeb60..572468ca8d 100644
--- Lib/unittest/mock.py
+++ Lib/unittest/mock.py
@@ -48,6 +48,8 @@ _safe_super = super
def _is_async_obj(obj):
if _is_instance_mock(obj) and not isinstance(obj, AsyncMock):
return False
+ if hasattr(obj, '__func__'):
+ obj = getattr(obj, '__func__')
return asyncio.iscoroutinefunction(obj) or inspect.isawaitable(obj) [0] Line 1387 in 50d4f12
[1] Line 48 in 50d4f12
|
Found Raymond's answer on the difference between __dict__ and attribute lookup regarding descriptors to be useful here : https://stackoverflow.com/a/44600603 |
I'd love to see this issue resolved, as it is keeping me from being able to switch to 3.8. I have a PR with Karthikeyan's suggestion, as I agree it makes more sense and could apply to more cases: #18116 |
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: