New submission from Karthikeyan Singaravelan <tir.kar...@gmail.com>:
* In create_autospec there is some logic to detect if the function is an async function this could be refactored out as a private function. * create_autospec has initialization code for async mock. For synchronous functions this is done with _setup_func and is called during setting signature. AsyncMock needs different setup logic code to setup the functions but the code can be refactored out of create_autospec into _setup_async_func. * In create_autospec awaited attribute is not set for AsyncMock during setup [0] and hence this causes AttributeError when the coroutine is awaited. awaited attribute can be also initialized. import asyncio from unittest.mock import create_autospec async def foo(): pass spec = create_autospec(foo) awaitable = spec() async def main(): await awaitable asyncio.run(main()) Traceback (most recent call last): File "/tmp/spam.py", line 13, in <module> asyncio.run(main()) File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/asyncio/base_events.py", line 614, in run_until_complete return future.result() File "/tmp/spam.py", line 11, in main await awaitable File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 2045, in _mock_call return await proxy() File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 2043, in proxy await self.awaited._notify() File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 596, in __getattr__ raise AttributeError("Mock object has no attribute %r" % name) AttributeError: Mock object has no attribute 'awaited' * In the setup logic to create attributes like assert_not_awaited uses the pattern setattr(mock, a, f) at [1] . But due to late binding 'a' in the function f has the last value of the loop 'assert_not_awaited' and hence calling other helpers also calls assert_not_awaited. This could be resolved by using a partial function that binds the attribute value early in the loop and respective function would be used in getattr. >>> spec.assert_awaited_once_with(1) # Due to late binding assert_not_awaited >>> is always called TypeError: assert_not_awaited() takes 1 positional argument but 2 were given * assert_not_awaited has the error message indicating it should be awaited once [2] . This can be changed to indicate something like "Expected mock to not have been awaited". >>> spec.assert_not_awaited() AssertionError: Expected mock to have been awaited once. Awaited 1 times. * mock docs have list of magic methods implemented where __aenter__, __aexit__, __aiter__ and __anext__ could be documented with versionadded directive. [3] I have a PR with the above changes that I will post shortly for review. [0] https://github.com/python/cpython/blob/7114c6504a60365b8b0cd718da0ec8a737599fb9/Lib/unittest/mock.py#L2506 [1] https://github.com/python/cpython/blob/7114c6504a60365b8b0cd718da0ec8a737599fb9/Lib/unittest/mock.py#L2518 [2] https://github.com/python/cpython/blob/7114c6504a60365b8b0cd718da0ec8a737599fb9/Lib/unittest/mock.py#L2154 [3] https://docs.python.org/3.8/library/unittest.mock.html#mocking-magic-methods ---------- components: Library (Lib) messages: 343504 nosy: asvetlov, cjw296, lisroach, mariocj89, michael.foord, xtreak, yselivanov priority: normal severity: normal status: open title: Refactor AsyncMock setup logic in create_autospec type: behavior versions: Python 3.8 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue37047> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com