Steven D'Aprano wrote: > *eureka moment* > > I can use introspection on the function directly to see > how many arguments it accepts, instead of actually > calling the function and trapping the exception.
For funsies, the function 'can_call' below takes a function 'f' and returns a new function 'g'. Calling 'g' with a set of arguments returns True if 'f' would take the arguments, otherwise it returns False. See the test case for an example of use. import new def noop(): pass def can_call(func): # Make a new function with the same signature # code(argcount, nlocals, stacksize, flags, codestring, constants, names, # varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]]) code = func.func_code new_code = new.code(code.co_argcount, code.co_nlocals, noop.func_code.co_stacksize, code.co_flags, noop.func_code.co_code, # don't do anything code.co_consts, code.co_names, code.co_varnames, code.co_filename, "can_call_" + code.co_name, code.co_firstlineno, noop.func_code.co_lnotab, # for line number info code.co_freevars, # Do I need to set cellvars? Don't think so. ) # function(code, globals[, name[, argdefs[, closure]]]) new_func = new.function(new_code, func.func_globals, "can_call_" + func.func_name, func.func_defaults) # Uses a static scope def can_call_func(*args, **kwargs): try: new_func(*args, **kwargs) except TypeError, err: return False return True try: can_call_func.__name__ = "can_call_" + func.__name__ except TypeError: # Can't change the name in Python 2.3 or earlier pass return can_call_func #### test def spam(x, y, z=4): raise AssertionError("Don't call me!") can_spam = can_call(spam) for (args, kwargs) in ( ((1,2), {}), ((1,), {}), ((1,), {"x": 2}), ((), {"x": 1, "y": 2}), ((), {"x": 1, "z": 2}), ((1,2,3), {}), ((1,2,3), {"x": 3}), ): can_spam_result = can_spam(*args, **kwargs) try: spam(*args, **kwargs) except AssertionError: could_spam = True except TypeError: could_spam = False if can_spam_result == could_spam: continue print "Failure:", repr(args), repr(kwargs) print "Could I call spam()?", could_spam print "Did I think I could?", can_spam_result print print "Done." > Still a good question though. Why is it TypeError? My guess - in most languages with types, functions are typed not only on "is callable" but on the parameter signature. For example, in C dalke% awk '{printf("%3d %s\n", NR, $0)}' tmp.c 1 2 int f(int x, int y) { 3 } 4 5 int g(int x) { 6 } 7 8 main() { 9 int (*func_ptr)(int, int); 10 func_ptr = f; 11 func_ptr = g; 12 } % cc tmp.c tmp.c: In function `main': tmp.c:11: warning: assignment from incompatible pointer type % 'Course the next question might be "then how about an ArgumentError which is a subclasss of TypeError?" Andrew [EMAIL PROTECTED] -- http://mail.python.org/mailman/listinfo/python-list