0001"""Robust apply mechanism.
0002
0003Provides a function 'call', which can sort out what arguments a given
0004callable object can take, and subset the given arguments to match only
0005those which are acceptable.
0006"""
0007
0008def function(receiver):
0009    """Get function-like callable object for given receiver.
0010
0011    returns (function_or_method, codeObject, fromMethod)
0012
0013    If fromMethod is true, then the callable already has its first
0014    argument bound.
0015    """
0016    if hasattr(receiver, '__call__'):
0017        # receiver is a class instance; assume it is callable.
0018        # Reassign receiver to the actual method that will be called.
0019        c = receiver.__call__
0020        if hasattr(c, 'im_func') or hasattr(c, 'im_code'):
0021            receiver = c
0022    if hasattr(receiver, 'im_func'):
0023        # receiver is an instance-method.
0024        return receiver, receiver.im_func.func_code, 1
0025    elif not hasattr(receiver, 'func_code'):
0026        raise ValueError(
0027            'unknown reciever type %s %s' % (receiver, type(receiver)))
0028    return receiver, receiver.func_code, 0
0029
0030
0031def robust_apply(receiver, signature, *arguments, **named):
0032    """Call receiver with arguments and appropriate subset of named.
0033    ``signature`` is the callable used to determine the call signature
0034    of the receiver, in case ``receiver`` is a callable wrapper of the
0035    actual receiver."""
0036    signature, code_object, startIndex = function(signature)
0037    acceptable = code_object.co_varnames[
0038        startIndex + len(arguments):
0039        code_object.co_argcount
0040        ]
0041    for name in code_object.co_varnames[
0042        startIndex:startIndex + len(arguments)
0043        ]:
0044        if named.has_key(name):
0045            raise TypeError(
0046                'Argument %r specified both positionally '
0047                'and as a keyword for calling %r'
0048                % (name, signature)
0049                )
0050    if not (code_object.co_flags & 8):
0051        # fc does not have a **kwds type parameter, therefore 
0052        # remove unacceptable arguments.
0053        for arg in named.keys():
0054            if arg not in acceptable:
0055                del named[arg]
0056    return receiver(*arguments, **named)