# Code is loosly based on decorator library by Michele Simionato
# http://www.phyast.pitt.edu/~micheles/python/documentation.html

import itertools
from inspect import getargspec, formatargspec


def decorator(caller):
    """ Decorate function with caller preserving original signature.  """
    def entangle(func):
        argnames, varargs, kwargs, defaults = getargspec(func)
        # Argument sink
        if kwargs is None:
            kwargs = "tg_kw"
        defval_count = itertools.count()

        def formatarg(arg):
            """ Change arguments to form name=value.  """
            if varargs and arg == argnames[0]:
                return arg
            else:
                return "%s=%s" % (arg, arg)

        full_sign = formatargspec(argnames, varargs, kwargs, defaults,
                        formatvalue=lambda value: "=defaults[%i]" % (
                        defval_count.next()))[1:-1]
        short_sign = formatargspec(argnames, varargs, kwargs, defaults,
                        formatarg=formatarg,
                        formatvalue=lambda value: "")[1:-1]
        exec_dict = dict(func=func, caller=caller, defaults=defaults or ())
        func_str = """
def %s(%s):
  return caller(func, %s)
""" % (func.__name__, full_sign, short_sign)
        exec func_str in exec_dict
        newfunc = exec_dict[func.__name__]
        newfunc.__doc__ = func.__doc__
        newfunc.__dict__ = func.__dict__
        return newfunc
    return entangle
