From 5ffba320051f474cad2796621bc648e0e66890dc Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Tue, 13 Dec 2022 09:35:28 +0100 Subject: [PATCH] Adjusted implementation and docs. --- Doc/library/inspect.rst | 6 ++---- Lib/inspect.py | 20 +++++++++----------- Lib/test/test_inspect.py | 6 ++++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 13917c5f7b8518..d07da184280643 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -345,8 +345,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object is a :term:`coroutine function` (a function defined with an :keyword:`async def` syntax), a :func:`functools.partial` - wrapping a :term:`coroutine function`, an instance of a class defining an - :keyword:`async def` ``__call__``, or a sync function marked with + wrapping a :term:`coroutine function`, or a sync function marked with :func:`markcoroutinefunction`. .. versionadded:: 3.5 @@ -356,8 +355,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): wrapped function is a :term:`coroutine function`. .. versionchanged:: 3.12 - Instances of classes defining an :keyword:`async def` ``__call__``, or - sync functions marked with :func:`markcoroutinefunction` now return + Sync functions marked with :func:`markcoroutinefunction` now return ``True``. diff --git a/Lib/inspect.py b/Lib/inspect.py index fec2e319f5a6fa..9b531d174102cb 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -395,6 +395,14 @@ def isgeneratorfunction(obj): # A marker for markcoroutinefunction and iscoroutinefunction. _is_coroutine_marker = object() +def _has_coroutine_mark(f): + while ismethod(f): + f = f.__func__ + f = functools._unwrap_partial(f) + if not (isfunction(f) or _signature_is_functionlike(f)): + return False + return getattr(f, "_is_coroutine_marker", None) is _is_coroutine_marker + def markcoroutinefunction(func): """ Decorator to ensure callable is recognised as a coroutine function. @@ -410,17 +418,7 @@ def iscoroutinefunction(obj): Coroutine functions are normally defined with "async def" syntax, but may be marked via markcoroutinefunction. """ - if not isclass(obj) and callable(obj): - # Test both the function and the __call__ implementation for the - # _is_coroutine_marker. - f = getattr(getattr(obj, "__func__", obj), "_is_coroutine_marker", None) - c = getattr(obj.__call__, "_is_coroutine_marker", None) - if f is _is_coroutine_marker or c is _is_coroutine_marker: - return True - - return _has_code_flag(obj, CO_COROUTINE) or ( - not isclass(obj) and callable(obj) and _has_code_flag(obj.__call__, CO_COROUTINE) - ) + return _has_code_flag(obj, CO_COROUTINE) or _has_coroutine_mark(obj) def isasyncgenfunction(obj): """Return true if the object is an asynchronous generator function. diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 490f34705c6570..f6dfc555e7983a 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -221,7 +221,8 @@ async def __call__(self): pass self.assertFalse(inspect.iscoroutinefunction(Cl)) - self.assertTrue(inspect.iscoroutinefunction(Cl())) + # instances with async def __call__ are NOT recognised. + self.assertFalse(inspect.iscoroutinefunction(Cl())) class Cl2: @inspect.markcoroutinefunction @@ -229,7 +230,8 @@ def __call__(self): pass self.assertFalse(inspect.iscoroutinefunction(Cl2)) - self.assertTrue(inspect.iscoroutinefunction(Cl2())) + # instances with marked __call__ are NOT recognised. + self.assertFalse(inspect.iscoroutinefunction(Cl2())) class Cl3: @inspect.markcoroutinefunction