Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.
Checking if something is callable or async callable in Python#
I wanted a mechanism to check if a given Python object was “callable” - could be called like a function - or “async callable” - could be called using await obj().
I ended up writing this helper function to help introspect objects and answer those questions:
1import asyncio2import types3from typing import NamedTuple, Any4
5
6class CallableStatus(NamedTuple):7 is_callable: bool8 is_async_callable: bool9
10
11def check_callable(obj: Any) -> CallableStatus:12 if not callable(obj):13 return CallableStatus(False, False)14
15 if isinstance(obj, type):16 # It's a class17 return CallableStatus(True, False)18
19 if isinstance(obj, types.FunctionType):20 return CallableStatus(True, asyncio.iscoroutinefunction(obj))21
22 if hasattr(obj, "__call__"):23 return CallableStatus(True, asyncio.iscoroutinefunction(obj.__call__))24
25 assert False, "obj {} is somehow callable with no __call__ method".format(repr(obj))check_callable(obj) returns a CallableStatus named tuple, with an is_callable boolean saying if it can be caled and an is_async_callable boolean specifying if you need to use await with it.
I wrote these pytest tests to exercise the check_callable() function:
1import pytest2
3
4class AsyncClass:5 async def __call__(self):6 pass7
8
9class NotAsyncClass:10 def __call__(self):11 pass12
13
14class ClassNoCall:15 pass16
17
18async def async_func():19 pass20
21
22def non_async_func():23 pass24
25
26@pytest.mark.parametrize(27 "obj,expected_is_callable,expected_is_async_callable",28 (29 (async_func, True, True),30 (non_async_func, True, False),31 (AsyncClass(), True, True),32 (NotAsyncClass(), True, False),33 (ClassNoCall(), False, False),34 (AsyncClass, True, False),35 (NotAsyncClass, True, False),36 (ClassNoCall, True, False),37 ("", False, False),38 (1, False, False),39 (str, True, False),40 ),41)42def test_check_callable(obj, expected_is_callable, expected_is_async_callable):43 status = check_callable(obj)44 assert status.is_callable == expected_is_callable45 assert status.is_async_callable == expected_is_async_callableSee this commit for my initial implementation.
Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.