Skip to content

Commit 9ebec7d

Browse files
gh-84753: Make inspect.iscoroutinefunction() work with AsyncMock (GH-94050) (GH-94460)
The inspect version was not working with unittest.mock.AsyncMock. The fix introduces special-casing of AsyncMock in `inspect.iscoroutinefunction` equivalent to the one performed in `asyncio.iscoroutinefunction`. Co-authored-by: Łukasz Langa <[email protected]> (cherry picked from commit 4261b6b) Co-authored-by: Mehdi ABAAKOUK <[email protected]>
1 parent 7fe949e commit 9ebec7d

File tree

5 files changed

+23
-1
lines changed

5 files changed

+23
-1
lines changed

Lib/inspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ def _has_code_flag(f, flag):
395395
while ismethod(f):
396396
f = f.__func__
397397
f = functools._unwrap_partial(f)
398-
if not isfunction(f):
398+
if not (isfunction(f) or _signature_is_functionlike(f)):
399399
return False
400400
return bool(f.__code__.co_flags & flag)
401401

Lib/test/test_asyncio/test_tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,7 @@ async def fn2():
16531653
self.assertTrue(asyncio.iscoroutinefunction(fn2))
16541654

16551655
self.assertFalse(asyncio.iscoroutinefunction(mock.Mock()))
1656+
self.assertTrue(asyncio.iscoroutinefunction(mock.AsyncMock()))
16561657

16571658
def test_coroutine_non_gen_function(self):
16581659
async def func():

Lib/test/test_inspect.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ def test_iscoroutine(self):
202202
gen_coroutine_function_example))))
203203
self.assertTrue(inspect.isgenerator(gen_coro))
204204

205+
self.assertFalse(
206+
inspect.iscoroutinefunction(unittest.mock.Mock()))
207+
self.assertTrue(
208+
inspect.iscoroutinefunction(unittest.mock.AsyncMock()))
205209
self.assertTrue(
206210
inspect.iscoroutinefunction(coroutine_function_example))
207211
self.assertTrue(
@@ -210,6 +214,10 @@ def test_iscoroutine(self):
210214
coroutine_function_example))))
211215
self.assertTrue(inspect.iscoroutine(coro))
212216

217+
self.assertFalse(
218+
inspect.isgeneratorfunction(unittest.mock.Mock()))
219+
self.assertFalse(
220+
inspect.isgeneratorfunction(unittest.mock.AsyncMock()))
213221
self.assertFalse(
214222
inspect.isgeneratorfunction(coroutine_function_example))
215223
self.assertFalse(
@@ -218,6 +226,12 @@ def test_iscoroutine(self):
218226
coroutine_function_example))))
219227
self.assertFalse(inspect.isgenerator(coro))
220228

229+
self.assertFalse(
230+
inspect.isasyncgenfunction(unittest.mock.Mock()))
231+
self.assertFalse(
232+
inspect.isasyncgenfunction(unittest.mock.AsyncMock()))
233+
self.assertFalse(
234+
inspect.isasyncgenfunction(coroutine_function_example))
221235
self.assertTrue(
222236
inspect.isasyncgenfunction(async_generator_function_example))
223237
self.assertTrue(

Lib/unittest/mock.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,10 @@ def __init__(self, /, *args, **kwargs):
21752175
code_mock = NonCallableMock(spec_set=CodeType)
21762176
code_mock.co_flags = inspect.CO_COROUTINE
21772177
self.__dict__['__code__'] = code_mock
2178+
self.__dict__['__name__'] = 'AsyncMock'
2179+
self.__dict__['__defaults__'] = tuple()
2180+
self.__dict__['__kwdefaults__'] = {}
2181+
self.__dict__['__annotations__'] = None
21782182

21792183
async def _execute_mock_call(self, /, *args, **kwargs):
21802184
# This is nearly just like super(), except for special handling
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`inspect.iscoroutinefunction` now properly returns ``True`` when an instance
2+
of :class:`unittest.mock.AsyncMock` is passed to it. This makes it consistent with
3+
behavior of :func:`asyncio.iscoroutinefunction`. Patch by Mehdi ABAAKOUK.

0 commit comments

Comments
 (0)