inspect.getargspec()
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." -----------------------------------------------
participants (5)
-
Guido van Rossum
-
holger krekel
-
Ka-Ping Yee
-
Patrick K. O'Brien
-
Tim Peters