diff --git a/param/_utils.py b/param/_utils.py index 2d0523d1..e8b1538f 100644 --- a/param/_utils.py +++ b/param/_utils.py @@ -208,18 +208,16 @@ def full_groupby(l, key=lambda x: x): def iscoroutinefunction(function): """ - Whether the function is an asynchronous coroutine function. - """ - if not hasattr(inspect, 'iscoroutinefunction'): - return False - import asyncio - try: - return ( - inspect.isasyncgenfunction(function) or - asyncio.iscoroutinefunction(function) - ) - except AttributeError: - return False + Whether the function is an asynchronous generator or a coroutine. + """ + # Partial unwrapping not required starting from Python 3.11.0 + # See https://github.com/holoviz/param/pull/894#issuecomment-1867084447 + while isinstance(function, functools.partial): + function = function.func + return ( + inspect.isasyncgenfunction(function) or + inspect.iscoroutinefunction(function) + ) def flatten(line): diff --git a/tests/testutils.py b/tests/testutils.py index 074b2ef8..e5228d4b 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -1,12 +1,14 @@ import datetime as dt import os +from functools import partial + import param import pytest from param import guess_param_types, resolve_path from param.parameterized import bothmethod -from param._utils import _is_mutable_container +from param._utils import _is_mutable_container, iscoroutinefunction try: @@ -393,3 +395,29 @@ class P(param.Parameterized): ) def test__is_mutable_container(obj, ismutable): assert _is_mutable_container(obj) is ismutable + + +async def coro(): + return + + +def test_iscoroutinefunction_coroutine(): + assert iscoroutinefunction(coro) + + +def test_iscoroutinefunction_partial_coroutine(): + pcoro = partial(partial(coro)) + assert iscoroutinefunction(pcoro) + + +async def agen(): + yield + + +def test_iscoroutinefunction_asyncgen(): + assert iscoroutinefunction(agen) + + +def test_iscoroutinefunction_partial_asyncgen(): + pagen = partial(partial(agen)) + assert iscoroutinefunction(pagen)