mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-94924: support inspect.iscoroutinefunction
in create_autospec(async_def)
(#94962)
* support inspect.iscoroutinefunction in create_autospec(async_def) * test create_autospec with inspect.iscoroutine and inspect.iscoroutinefunction * test when create_autospec functions check their signature
This commit is contained in:
parent
0f885ffa94
commit
9bf8d825a6
3 changed files with 54 additions and 2 deletions
|
@ -232,7 +232,9 @@ class AsyncAutospecTest(unittest.TestCase):
|
||||||
run(main())
|
run(main())
|
||||||
|
|
||||||
self.assertTrue(iscoroutinefunction(spec))
|
self.assertTrue(iscoroutinefunction(spec))
|
||||||
|
self.assertTrue(inspect.iscoroutinefunction(spec))
|
||||||
self.assertTrue(asyncio.iscoroutine(awaitable))
|
self.assertTrue(asyncio.iscoroutine(awaitable))
|
||||||
|
self.assertTrue(inspect.iscoroutine(awaitable))
|
||||||
self.assertEqual(spec.await_count, 1)
|
self.assertEqual(spec.await_count, 1)
|
||||||
self.assertEqual(spec.await_args, call(1, 2, c=3))
|
self.assertEqual(spec.await_args, call(1, 2, c=3))
|
||||||
self.assertEqual(spec.await_args_list, [call(1, 2, c=3)])
|
self.assertEqual(spec.await_args_list, [call(1, 2, c=3)])
|
||||||
|
@ -244,6 +246,25 @@ class AsyncAutospecTest(unittest.TestCase):
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
spec.assert_any_await(e=1)
|
spec.assert_any_await(e=1)
|
||||||
|
|
||||||
|
def test_autospec_checks_signature(self):
|
||||||
|
spec = create_autospec(async_func_args)
|
||||||
|
# signature is not checked when called
|
||||||
|
awaitable = spec()
|
||||||
|
self.assertListEqual(spec.mock_calls, [])
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await awaitable
|
||||||
|
|
||||||
|
# but it is checked when awaited
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
run(main())
|
||||||
|
|
||||||
|
# _checksig_ raises before running or awaiting the mock
|
||||||
|
self.assertListEqual(spec.mock_calls, [])
|
||||||
|
self.assertEqual(spec.await_count, 0)
|
||||||
|
self.assertIsNone(spec.await_args)
|
||||||
|
self.assertEqual(spec.await_args_list, [])
|
||||||
|
spec.assert_not_awaited()
|
||||||
|
|
||||||
def test_patch_with_autospec(self):
|
def test_patch_with_autospec(self):
|
||||||
|
|
||||||
|
@ -253,7 +274,9 @@ class AsyncAutospecTest(unittest.TestCase):
|
||||||
self.assertIsInstance(mock_method.mock, AsyncMock)
|
self.assertIsInstance(mock_method.mock, AsyncMock)
|
||||||
|
|
||||||
self.assertTrue(iscoroutinefunction(mock_method))
|
self.assertTrue(iscoroutinefunction(mock_method))
|
||||||
|
self.assertTrue(inspect.iscoroutinefunction(mock_method))
|
||||||
self.assertTrue(asyncio.iscoroutine(awaitable))
|
self.assertTrue(asyncio.iscoroutine(awaitable))
|
||||||
|
self.assertTrue(inspect.iscoroutine(awaitable))
|
||||||
self.assertTrue(inspect.isawaitable(awaitable))
|
self.assertTrue(inspect.isawaitable(awaitable))
|
||||||
|
|
||||||
# Verify the default values during mock setup
|
# Verify the default values during mock setup
|
||||||
|
|
|
@ -204,6 +204,33 @@ def _set_signature(mock, original, instance=False):
|
||||||
_setup_func(funcopy, mock, sig)
|
_setup_func(funcopy, mock, sig)
|
||||||
return funcopy
|
return funcopy
|
||||||
|
|
||||||
|
def _set_async_signature(mock, original, instance=False, is_async_mock=False):
|
||||||
|
# creates an async function with signature (*args, **kwargs) that delegates to a
|
||||||
|
# mock. It still does signature checking by calling a lambda with the same
|
||||||
|
# signature as the original.
|
||||||
|
|
||||||
|
skipfirst = isinstance(original, type)
|
||||||
|
result = _get_signature_object(original, instance, skipfirst)
|
||||||
|
if result is None:
|
||||||
|
return mock
|
||||||
|
func, sig = result
|
||||||
|
def checksig(*args, **kwargs):
|
||||||
|
sig.bind(*args, **kwargs)
|
||||||
|
_copy_func_details(func, checksig)
|
||||||
|
|
||||||
|
name = original.__name__
|
||||||
|
if not name.isidentifier():
|
||||||
|
name = 'funcopy'
|
||||||
|
context = {'_checksig_': checksig, 'mock': mock}
|
||||||
|
src = """async def %s(*args, **kwargs):
|
||||||
|
_checksig_(*args, **kwargs)
|
||||||
|
return await mock(*args, **kwargs)""" % name
|
||||||
|
exec (src, context)
|
||||||
|
funcopy = context[name]
|
||||||
|
_setup_func(funcopy, mock, sig)
|
||||||
|
_setup_async_mock(funcopy)
|
||||||
|
return funcopy
|
||||||
|
|
||||||
|
|
||||||
def _setup_func(funcopy, mock, sig):
|
def _setup_func(funcopy, mock, sig):
|
||||||
funcopy.mock = mock
|
funcopy.mock = mock
|
||||||
|
@ -2745,9 +2772,10 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
|
||||||
if isinstance(spec, FunctionTypes):
|
if isinstance(spec, FunctionTypes):
|
||||||
# should only happen at the top level because we don't
|
# should only happen at the top level because we don't
|
||||||
# recurse for functions
|
# recurse for functions
|
||||||
mock = _set_signature(mock, spec)
|
|
||||||
if is_async_func:
|
if is_async_func:
|
||||||
_setup_async_mock(mock)
|
mock = _set_async_signature(mock, spec)
|
||||||
|
else:
|
||||||
|
mock = _set_signature(mock, spec)
|
||||||
else:
|
else:
|
||||||
_check_signature(spec, mock, is_type, instance)
|
_check_signature(spec, mock, is_type, instance)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
:func:`unittest.mock.create_autospec` now properly returns coroutine functions compatible with :func:`inspect.iscoroutinefunction`
|
Loading…
Add table
Add a link
Reference in a new issue