Source code for qcodes.utils.function_helpers
from inspect import CO_VARARGS, iscoroutinefunction, signature
[docs]
def is_function(f: object, arg_count: int, coroutine: bool | None = False) -> bool:
"""
Check and require a function that can accept the specified number of
positional arguments, which either is or is not a coroutine
type casting "functions" are allowed, but only in the 1-argument form.
Args:
f: Function to check.
arg_count: Number of argument f should accept.
coroutine: Is a coroutine.
Return:
bool: is function and accepts the specified number of arguments.
"""
if not isinstance(arg_count, int) or arg_count < 0:
raise TypeError("arg_count must be a non-negative integer")
if not callable(f):
return False
if coroutine is not None:
if bool(coroutine) is not iscoroutinefunction(f):
return False
if isinstance(f, type):
# for type casting functions, eg int, str, float
# only support the one-parameter form of these,
# otherwise the user should make an explicit function.
return arg_count == 1
if func_code := getattr(f, "__code__", None):
# handle objects like functools.partial(f, ...)
func_defaults = getattr(f, "__defaults__", None)
number_of_defaults = len(func_defaults) if func_defaults is not None else 0
if getattr(f, "__self__", None) is not None:
# bound method
min_positional = func_code.co_argcount - 1 - number_of_defaults
max_positional = func_code.co_argcount - 1
else:
min_positional = func_code.co_argcount - number_of_defaults
max_positional = func_code.co_argcount
if func_code.co_flags & CO_VARARGS:
# we have *args
max_positional = 10e10
ev = min_positional <= arg_count <= max_positional
return ev
try:
sig = signature(f)
except ValueError:
# some built-in functions/methods don't describe themselves to inspect
# we already know it's a callable and coroutine is correct.
return True
try:
inputs = [0] * arg_count
sig.bind(*inputs)
return True
except TypeError:
return False