[Python-Dev] inspect.getargspec()
Patrick K. O'Brien
pobrien@orbtech.com
Wed, 6 Nov 2002 18:54:19 -0600
I was patching up some bugs in the inspect module, and I've reached a
particular bug where I'd like some advice. The bug on SF is # 620190 and it
relates to the fact that inspect.getargspec() fails when called with a
method, rather than a function. I've got the code that fixes this (it's
actually been in PyCrust for a while) but fixing it also changes the
semantics, because getargspec() is clear that it only works on functions.
Of course, expanding getargspec() to work on any callable is fairly
trivial, so I see no reason why it should be limited to functions.
In any case, I'll outline some of the issues below and post the code I've
been using in PyCrust. I'm hoping that someone from the inner circle will
work with me to update the module, update the unit tests, and check the
changes in (or create patches). I'd hate to work up a patch just to find
out that my solution wasn't appropriate.
The main issues are:
* Is there any reason to not expand the scope of getargspec()?
* For builtin functions whose arguments cannot be determined (at least not
that I know of) what should getargspec() do? Should it raise an error or
return empty values?
* For a bound method, should getargspec() include the first argument
(usually self) since Python passes that value implicitly? Perhaps there
should be a optional argument to toggle this.
Here is what the current getargspec() looks like:
def getargspec(func):
"""Get the names and default values of a function's arguments.
A tuple of four things is returned: (args, varargs, varkw, defaults).
'args' is a list of the argument names (it may contain nested lists).
'varargs' and 'varkw' are the names of the * and ** arguments or None.
'defaults' is an n-tuple of the default values of the last n
arguments."""
if not isfunction(func): raise TypeError, 'arg is not a Python function'
args, varargs, varkw = getargs(func.func_code)
return args, varargs, varkw, func.func_defaults
Here is what I use in PyCrust to get the argument spec for any callable (At
least the ones that have an argspec. Builtin functions are still an issue.)
Basically, I take the object in question and run it through the following
function to get an object that will work with inspect.getargspec(). I'm
also determining whether I should drop the first argument (self) when I
display the calltip for this object, since Python passes that implicitly on
bound methods:
def getBaseObject(object):
"""Return base object and dropSelf indicator for an object."""
if inspect.isbuiltin(object):
# Builtin functions don't have an argspec that we can get.
dropSelf = 0
elif inspect.ismethod(object):
# Get the function from the object otherwise inspect.getargspec()
# complains that the object isn't a Python function.
try:
if object.im_self is None:
# This is an unbound method so we do not drop self from the
# argspec, since an instance must be passed as the first
arg.
dropSelf = 0
else:
dropSelf = 1
object = object.im_func
except AttributeError:
dropSelf = 0
elif inspect.isclass(object):
# Get the __init__ method function for the class.
constructor = getConstructor(object)
if constructor is not None:
object = constructor
dropSelf = 1
else:
dropSelf = 0
elif callable(object):
# Get the __call__ method instead.
try:
object = object.__call__.im_func
dropSelf = 1
except AttributeError:
dropSelf = 0
else:
dropSelf = 0
return object, dropSelf
def getConstructor(object):
"""Return constructor for class object, or None if there isn't one."""
try:
return object.__init__.im_func
except AttributeError:
for base in object.__bases__:
constructor = getConstructor(base)
if constructor is not None:
return constructor
return None
And here is a snippet of the code that makes use of the previous functions,
just for completeness:
name = ''
object, dropSelf = getBaseObject(object)
try:
name = object.__name__
except AttributeError:
pass
tip1 = ''
argspec = ''
if inspect.isbuiltin(object):
# Builtin functions don't have an argspec that we can get.
pass
elif inspect.isfunction(object):
# tip1 is a string like: "getCallTip(command='', locals=None)"
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
if dropSelf:
# The first parameter to a method is a reference to an
# instance, usually coded as "self", and is usually passed
# automatically by Python and therefore we want to drop it.
temp = argspec.split(',')
if len(temp) == 1: # No other arguments.
argspec = '()'
else: # Drop the first argument.
argspec = '(' + ','.join(temp[1:]).lstrip()
tip1 = name + argspec
Thanks in advance for the help.
--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------