This commit is contained in:
Akadiri Oluwagbemi Enoch 2026-10-07 16:42:45 +08:00 • committed by GitHub
commit 8b395ea137
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 3 deletions

View file

@ -1,5 +1,4 @@
import inspect
import types
from typing import Any, NamedTuple
@ -16,8 +15,11 @@ def check_callable(obj: Any) -> CallableStatus:
# It's a class
return CallableStatus(True, False)
if isinstance(obj, types.FunctionType):
return CallableStatus(True, inspect.iscoroutinefunction(obj))
if inspect.iscoroutinefunction(obj):
# Inspect the object itself, so bound methods and functools.partial objects
# wrapping an async function are recognised - they are not types.FunctionType,
# and their __call__ is a method wrapper rather than a coroutine function.
return CallableStatus(True, True)
if callable(obj):
return CallableStatus(True, inspect.iscoroutinefunction(obj.__call__))

View file

@ -1,3 +1,5 @@
import functools
import pytest
from datasette.utils.check_callable import check_callable
@ -17,6 +19,14 @@ class ClassNoCall:
pass
class MethodHolder:
async def async_method(self):
pass
def sync_method(self):
pass
async def async_func():
pass
@ -39,6 +49,12 @@ def non_async_func():
("", False, False),
(1, False, False),
(str, True, False),
# Bound methods and functools.partial objects are not types.FunctionType,
# but they are still async when the function they wrap is
(MethodHolder().async_method, True, True),
(MethodHolder().sync_method, True, False),
(functools.partial(async_func), True, True),
(functools.partial(non_async_func), True, False),
),
)
def test_check_callable(obj, expected_is_callable, expected_is_async_callable):