The best way I can come up with for this is to create a new function with the same signatures and default values ββand try to call this and catch the exception. The body of the new function code should be pass so that there are no side effects. There is nothing stupid here, the code is simply tiresome. It is worth noting that this connects you to internal CPython. It works on 2.6. You will have to port it to other versions, but it should not be too complicated.
import types ARGS_FLAG = 4 #If memory serves, this can be found in code.h in the Python source. KWARGS_FLAG = 8 def valid(f, args, kwargs): def dummy(): pass dummy_code = dummy.func_code real_code = f.func_code args_flag = real_code.co_flags & ARGS_FLAG kwargs_flag = real_code.co_flags & KWARGS_FLAG # help(types.CodeType) for details test_code = types.CodeType(real_code.co_argcount, real_code.co_nlocals, dummy_code.co_stacksize, args_flag | kwargs_flag, dummy_code.co_code, dummy_code.co_consts, dummy_code.co_names, real_code.co_varnames, "<test>", "", 0, "", ()) # help(types.FunctionType) for details test_func = types.FunctionType(test_code, {}, "test", f.func_defaults) try: test_func(*args, **kwargs) except TypeError: return False else: return True def x(a, b, c): pass def y(a, b=1, c=2): pass def z(a=1, b=2, c=3): pass print valid(x, ("hello", "goodbye", "what?"), {}) # => True print valid(x, ("hello", "goodbye"), {}) # => False print valid(y, ("hello", "goodbye", "what?"), {}) # => True print valid(y, (), {"a": "hello", "b": "goodbye", "c": "what"}) #=> True print valid(y, ("hello", "goodbye"), {"c": "what?"}) #=> True
Running this code gives:
$ python argspec.py True False True True True