"""
Provides usable args and kwargs from inspect.getcallargs.

For Python 3.3 and above, this module is unnecessary and can be achieved using
features from PEP 362:

    http://www.python.org/dev/peps/pep-0362/

For example, to override a parameter of some function:

    >>> import inspect
    >>> def func(a, b=1, c=2, d=3):
    ...    return a, b, c, d
    ...
    >>> def override_c(*args, **kwargs):
    ...    sig = inspect.signature(override)
    ...    ba = sig.bind(*args, **kwargs)
    ...    ba['c'] = 10
    ...    return func(*ba.args, *ba.kwargs)
    ...
    >>> override_c(0, c=3)
    (0, 1, 10, 3)

Also useful:

    http://www.python.org/dev/peps/pep-3102/

"""

import sys
import inspect

from inspect import getcallargs


try:
    from inspect import getfullargspec
except ImportError:
    # Python 2.X
    from collections import namedtuple
    from inspect import getargspec

    FullArgSpec = namedtuple('FullArgSpec',
    'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations')

    def getfullargspec(f):
        args, varargs, varkw, defaults = getargspec(f)
        kwonlyargs = []
        kwonlydefaults = None
        annotations = getattr(f, '__annotations__', {})
        return FullArgSpec(args, varargs, varkw, defaults,
                           kwonlyargs, kwonlydefaults, annotations)


def bindcallargs_leq32(_fUnCtIoN_, *args, **kwargs):
    """Binds arguments and keyword arguments to a function or method.

    Returns a tuple (bargs, bkwargs) suitable for manipulation and passing
    to the specified function.

    `bargs` consists of the bound args, varargs, and kwonlyargs from
    getfullargspec. `bkwargs` consists of the bound varkw from getfullargspec.
    Both can be used in a call to the specified function.  Any default
    parameter values are included in the output.

    Examples
    --------
    >>> def func(a, b=3, *args, **kwargs):
    ...    pass

    >>> bindcallargs(func, 5)
    ((5, 3), {})

    >>> bindcallargs(func, 5, 4, 3, 2, 1, hello='there')
    ((5, 4, 3, 2, 1), {'hello': 'there'})

    >>> args, kwargs = bindcallargs(func, 5)
    >>> kwargs['b'] = 5 # overwrite default value for b
    >>> func(*args, **kwargs)

    """
    # It is necessary to choose an unlikely variable name for the function.
    # The reason is that any kwarg by the same name will cause a TypeError
    # due to multiple values being passed for that argument name.
    func = _fUnCtIoN_

    callargs = getcallargs(func, *args, **kwargs)
    spec = getfullargspec(func)

    # Construct all args and varargs and use them in bargs
    bargs = [callargs[arg] for arg in spec.args]
    if spec.varargs is not None:
        bargs.extend(callargs[spec.varargs])
    bargs = tuple(bargs)

    # Start with kwonlyargs.
    bkwargs = {kwonlyarg: callargs[kwonlyarg] for kwonlyarg in spec.kwonlyargs}
    # Add in kwonlydefaults for unspecified kwonlyargs only.
    # Since keyword only arguements aren't allowed in python2, and we
    # don't support python 3.0, 3.1, 3.2, this should never be executed:
    if spec.kwonlydefaults is not None:  # pragma: no cover
        bkwargs.update({k: v for k, v in spec.kwonlydefaults.items()
                             if k not in bkwargs})
    # Add in varkw.
    if spec.varkw is not None:
        bkwargs.update(callargs[spec.varkw])

    return bargs, bkwargs


def bindcallargs_geq33(_fUnCtIoN_, *args, **kwargs):
    # Should match functionality of bindcallargs_32 for Python > 3.3.
    sig = inspect.signature(_fUnCtIoN_)
    ba = sig.bind(*args, **kwargs)
    # Add in all default values
    for param in sig.parameters.values():
        if param.name not in ba.arguments:
            ba.arguments[param.name] = param.default
    return ba.args, ba.kwargs

if sys.version_info[0:2] < (3,3):
    bindcallargs = bindcallargs_leq32
else:
    bindcallargs = bindcallargs_geq33

[((87, 15, 87, 49), 'inspect.getcallargs', 'getcallargs', ({(87, 27, 87, 31): 'func', (87, 33, 87, 38): '*args'}, {}), '(func, *args, **kwargs)', False, 'from inspect import getcallargs\n'), ((88, 11, 88, 31), 'inspect.getfullargspec', 'getfullargspec', ({(88, 26, 88, 30): 'func'}, {}), '(func)', False, 'from inspect import getfullargspec\n'), ((113, 10, 113, 39), 'inspect.signature', 'inspect.signature', ({(113, 28, 113, 38): '_fUnCtIoN_'}, {}), '(_fUnCtIoN_)', False, 'import inspect\n'), ((43, 18, 44, 78), 'collections.namedtuple', 'namedtuple', ({(43, 29, 43, 42): '"""FullArgSpec"""', (44, 4, 44, 77): '"""args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations"""'}, {}), "('FullArgSpec',\n    'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations')", False, 'from collections import namedtuple\n'), ((47, 41, 47, 54), 'inspect.getargspec', 'getargspec', ({(47, 52, 47, 53): 'f'}, {}), '(f)', False, 'from inspect import getargspec\n')]