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