Incorrect number of arguments
Andrew Dalke
dalke at dalkescientific.com
Thu Jun 9 03:51:32 EDT 2005
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
dalke at dalkescientific.com
More information about the Python-list
mailing list