Skip to content

Commit 5ffba32

Browse files
committed
Adjusted implementation and docs.
1 parent c073cf7 commit 5ffba32

File tree

3 files changed

+15
-17
lines changed

3 files changed

+15
-17
lines changed

Doc/library/inspect.rst

+2-4
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
345345

346346
Return ``True`` if the object is a :term:`coroutine function` (a function
347347
defined with an :keyword:`async def` syntax), a :func:`functools.partial`
348-
wrapping a :term:`coroutine function`, an instance of a class defining an
349-
:keyword:`async def` ``__call__``, or a sync function marked with
348+
wrapping a :term:`coroutine function`, or a sync function marked with
350349
:func:`markcoroutinefunction`.
351350

352351
.. versionadded:: 3.5
@@ -356,8 +355,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
356355
wrapped function is a :term:`coroutine function`.
357356

358357
.. versionchanged:: 3.12
359-
Instances of classes defining an :keyword:`async def` ``__call__``, or
360-
sync functions marked with :func:`markcoroutinefunction` now return
358+
Sync functions marked with :func:`markcoroutinefunction` now return
361359
``True``.
362360

363361

Lib/inspect.py

+9-11
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,14 @@ def isgeneratorfunction(obj):
395395
# A marker for markcoroutinefunction and iscoroutinefunction.
396396
_is_coroutine_marker = object()
397397

398+
def _has_coroutine_mark(f):
399+
while ismethod(f):
400+
f = f.__func__
401+
f = functools._unwrap_partial(f)
402+
if not (isfunction(f) or _signature_is_functionlike(f)):
403+
return False
404+
return getattr(f, "_is_coroutine_marker", None) is _is_coroutine_marker
405+
398406
def markcoroutinefunction(func):
399407
"""
400408
Decorator to ensure callable is recognised as a coroutine function.
@@ -410,17 +418,7 @@ def iscoroutinefunction(obj):
410418
Coroutine functions are normally defined with "async def" syntax, but may
411419
be marked via markcoroutinefunction.
412420
"""
413-
if not isclass(obj) and callable(obj):
414-
# Test both the function and the __call__ implementation for the
415-
# _is_coroutine_marker.
416-
f = getattr(getattr(obj, "__func__", obj), "_is_coroutine_marker", None)
417-
c = getattr(obj.__call__, "_is_coroutine_marker", None)
418-
if f is _is_coroutine_marker or c is _is_coroutine_marker:
419-
return True
420-
421-
return _has_code_flag(obj, CO_COROUTINE) or (
422-
not isclass(obj) and callable(obj) and _has_code_flag(obj.__call__, CO_COROUTINE)
423-
)
421+
return _has_code_flag(obj, CO_COROUTINE) or _has_coroutine_mark(obj)
424422

425423
def isasyncgenfunction(obj):
426424
"""Return true if the object is an asynchronous generator function.

Lib/test/test_inspect.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,17 @@ async def __call__(self):
221221
pass
222222

223223
self.assertFalse(inspect.iscoroutinefunction(Cl))
224-
self.assertTrue(inspect.iscoroutinefunction(Cl()))
224+
# instances with async def __call__ are NOT recognised.
225+
self.assertFalse(inspect.iscoroutinefunction(Cl()))
225226

226227
class Cl2:
227228
@inspect.markcoroutinefunction
228229
def __call__(self):
229230
pass
230231

231232
self.assertFalse(inspect.iscoroutinefunction(Cl2))
232-
self.assertTrue(inspect.iscoroutinefunction(Cl2()))
233+
# instances with marked __call__ are NOT recognised.
234+
self.assertFalse(inspect.iscoroutinefunction(Cl2()))
233235

234236
class Cl3:
235237
@inspect.markcoroutinefunction

0 commit comments

Comments
 (0)