Fredrik said:
Fredrik said:
If I have a variable which points to a function, can I check if certain
argument list matches what the function wants before or when calling it?
Currently, I'm trying to catch a TypeError when calling the function
(since that is what is raised when trying to call it with an illegal
list), but that has the rather undesirable side effect of also catching
any TypeErrors raised inside the function. Is there a way to avoid that?
Fredrik Tolf
Since python is "weakly typed", you will not be able to check what
"type" of arguments a function expects. [...]
Sorry, it seems I made myself misunderstood. I don't intend to check the
type of the values that I pass to a function (I'm well aware that Python
is dynamically typed). The reason I refer to TypeError is because that
seems to be the exception raised by Python when I attempt to call a
function with an argument list that wouldn't be valid for it (for
example due to the number of parameters being wrong). I just want to
check in advance whether a certain arglist would be valid to call a
certain function.
So, if you want to know the number of
arguments expected, I've found this works:
py> def func(a,b,c):
... print a,b,c
...
py> func.func_code.co_argcount
3
Not very well, though:
...
2
Here, that would indicate that I could only call `a' with two arguments,
while in fact I could call it with two or more arguments.
More exactly, what I'm trying to do is this; I writing a small protocol
server class as part of a program, and I thought it would be convenient
to be able to define new commands in the protocol by just adding a
function for them, like this:
class client:
# ...
# Read is called from the event driver when data is available on the
# socket.
def read(self):
self.buf += self.sk.recv(65536)
for c in self.buf.split("\n")[:-1]:
cv = tokenize(c)
if len(cv) > 0:
f = getattr(self, "cmd_" + cv[0], None)
if callable(f):
f(*cv[1:])
else:
self.reply("err", "unk")
# Example:
def cmd_echo(self, arg1):
self.reply("ok", arg1)
So basically, I want to be able to handle to client giving the wrong
number of arguments and reply with an error rather than the server
process crashing and dying. Therefore, I'm currently catching TypeErrors
when calling the function:
# ...
if callable(f):
try:
f(*cv[1:])
except TypeError:
self.reply("err", "argno")
# ...
It might be more reasonable to program your cmd_* functions to be more
flexible themselves. This would be more pythonic. E.g.
def cmd_echo(self, *args):
if not len(args):
answer = 'OMITTED' # or raise your own exception
else:
answer = args[0]
self.reply("ok", answer)
Now, you never have to worry about length of the argument list.
You could, of course factor this behavior. Here's how I might do it:
# first make a custom exception class, called whatever you want
class ServerError(exception): pass
#... somewhere in your client class
def preprocess_args(self, args, numexpected):
"""
This is the more flexible way.
A less flexible way would to check for the exact number of
arguments. But that goes against the "lenient in what you accept"
maxim.
"""
if len(args) < numexpected:
raise ServerError, 'Too few args!'
else:
return args[:numexpected]
def cmd_echo(self, *args):
(answer,) = preprocess_args(args, 1) # preprocess returns tuple
self.reply("ok", answer)
#... elsewhere in your client class
if callable(f):
try:
f(*cv[1:])
except ServerError, e:
self.reply("err", "argno") # can also make use of exception e
You could factor even further with a dictionary:
minargs = {'echo' : 1, 'etc' : 5000}
#... somewhere in your client class
def preprocess_args(self, cv):
"""
This is the more flexible way.
A less flexible way would to check for the exact number of
arguments. But that goes against the "lenient in what you accept"
maxim.
"""
args = cv[1:]
expected = minargs[cv[0]]
if len(args) < expected:
raise ServerError, 'Too few args!'
else:
return args[:expected]
# ...
def cmd_echo(self, answer):
self.reply("ok", answer)
# ... elsewhere
if callable(f):
try:
args = preprocess_args(cv)
except ServerError, e:
self.reply("err", "argno") # can also make use of exception e
f(*args)
Now you shouldn't have to rewrite any cmd_* functions.
James
--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095
http://www.jamesstroud.com/