Excellent question. I just had the problem that I wanted to write a function that takes a callback argument. Depending on the number of arguments of that callback, it needs to be called differently.
I started with gimel's answer, then expanded to be able to deal with builtins which don't react well with the inspect
module (raise TypeError
).
So here's code to check if a function expects exactly one argument:
def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False):
"""True if given func expects only one argument
Example (testbench):
assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args'
assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg'
assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args'
assert not func_has_one_arg_only(lambda *a: k), 'lambda *a'
assert not func_has_one_arg_only(lambda **a: k), 'lambda **a'
assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a'
assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a'
assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg'
assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args'
assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a'
assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a'
assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a'
assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a'
"""
try:
import inspect
argspec = inspect.getargspec(func)
except TypeError: # built-in c-code (e.g. dict.__getitem__)
try:
func(typical_argument)
except TypeError:
return False
else:
return True
else:
if not ignore_varargs:
if argspec.varargs or argspec.keywords:
return False
if 1 == len(argspec.args):
return True
return False
raise RuntimeError('This line should not be reached')
You can control the behaviour related to varargs arguments *args
and **kwargs
with the ignore_varargs
parameter.
The typical_argument
parameter is a kludge: If inspect
fails to work, e.g. on the aforementioned builtins, then we just try to call the function with one argument and see what happens.
The problem with this approach is that there are multiple reasons to raise TypeError
: Either the wrong number of arguments is used, or the wrong type of arguments is used. By allowing the user to provide a typical_argument
I'm trying to circumvent this issue.
This is not nice. But it may help folks having the same question and also running into the fact that inspect
cannot inspect C-coded function implementations. Maybe folks have a better suggestion?