mirror of
https://github.com/simonw/datasette.git
synced 2026-10-18 15:26:57 +02:00
Fix check_callable() reporting wrapped async callables as synchronous
check_callable() only inspected objects that are exactly types.FunctionType, and otherwise looked at obj.__call__. For a bound async method or a functools.partial wrapping an async function, __call__ is a method wrapper rather than a coroutine function, so both were reported as synchronous even though inspect.iscoroutinefunction() agrees they are async. Inspect the object itself first, which covers functions, bound methods and partial objects, and keep the __call__ check for callable instances.
This commit is contained in:
parent
cec5e6b2ef
commit
aafac649cb
2 changed files with 21 additions and 3 deletions
|
|
@ -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__))
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue