New submission from Karthikeyan Singaravelan <[email protected]>:
* 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 <[email protected]>
<https://bugs.python.org/issue37047>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com