1"""Robust apply mechanism 2 3Provides a function "call", which can sort out 4what arguments a given callable object can take, 5and subset the given arguments to match only 6those which are acceptable. 7""" 8import sys 9if sys.hexversion >= 0x3000000: 10 im_func = '__func__' 11 im_self = '__self__' 12 im_code = '__code__' 13 func_code = '__code__' 14else: 15 im_func = 'im_func' 16 im_self = 'im_self' 17 im_code = 'im_code' 18 func_code = 'func_code' 19 20 21def function(receiver): 22 """Get function-like callable object for given receiver 23 24 returns (function_or_method, codeObject, fromMethod) 25 26 If fromMethod is true, then the callable already 27 has its first argument bound 28 """ 29 if hasattr(receiver, '__call__'): 30 # Reassign receiver to the actual method that will be called. 31 if hasattr(receiver.__call__, im_func) or hasattr(receiver.__call__, 32 im_code): 33 receiver = receiver.__call__ 34 if hasattr(receiver, im_func): 35 # an instance-method... 36 return receiver, getattr(getattr(receiver, im_func), func_code), 1 37 elif not hasattr(receiver, func_code): 38 raise ValueError('unknown receiver type %s %s' % (receiver, 39 type(receiver))) 40 return receiver, getattr(receiver, func_code), 0 41 42 43def robustApply(receiver, *arguments, **named): 44 """Call receiver with arguments and an appropriate subset of named 45 """ 46 receiver, codeObject, startIndex = function(receiver) 47 acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount] 48 for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]: 49 if name in named: 50 raise TypeError( 51 """Argument %r specified both positionally and as a keyword""" 52 """ for calling %r""" % (name, receiver) 53 ) 54 if not (codeObject.co_flags & 8): 55 # fc does not have a **kwds type parameter, therefore 56 # remove unacceptable arguments. 57 for arg in list(named): 58 if arg not in acceptable: 59 del named[arg] 60 return receiver(*arguments, **named) 61