Decorate a function that takes strings to one that takes typed values.

    The decorator's arguments are functions to perform type conversion.
    The positional and keyword arguments will be mapped to the positional and
    keyword arguments of the decoratored function.  This allows web-based
    service functions, which by design always are passed string arguments, to be
    declared as functions taking typed arguments instead, eliminating
    the overhead of having to perform type conversions manually.

    If type conversion fails for any argument, the wrapped function will return
    a dict describing the exception that was raised.

def types(**typefuncs):
    """
    Decorate a function that takes strings to one that takes typed values.

    The decorator's arguments are functions to perform type conversion.
    The positional and keyword arguments will be mapped to the positional and
    keyword arguments of the decoratored function.  This allows web-based
    service functions, which by design always are passed string arguments, to be
    declared as functions taking typed arguments instead, eliminating
    the overhead of having to perform type conversions manually.

    If type conversion fails for any argument, the wrapped function will return
    a dict describing the exception that was raised.
    """
    def wrap(f):
        @functools.wraps(f)
        def typed_func(*pargs, **kwargs):
            # Analyze the incoming arguments so we know how to apply the
            # type-conversion functions in `typefuncs`.
            argspec = inspect.getargspec(f)

            # The `args` property contains the list of named arguments passed to
            # f.  Construct a dict mapping from these names to the values that
            # were passed.
            #
            # It is possible that `args` contains names that are not represented
            # in `pargs`, if some of the arguments are passed as keyword
            # arguments.  In this case, the relative shortness of `pargs` will
            # cause the call to zip() to truncate the `args` list, and the
            # keyword-style passed arguments will simply be present in `kwargs`.
            pargs_dict = {name: value for (name, value) in zip(argspec.args, pargs)}

            # Begin converting arguments according to the functions given in
            # `typefuncs`.  If a given name does not appear in `typefuncs`,
            # simply leave it unchanged.  If a name appears in `typefuncs` that
            # does not appear in the argument list, this is considered an error.
            try:
                for name, func in typefuncs.iteritems():
                    if name in pargs_dict:
                        pargs_dict[name] = func(pargs_dict[name])
                    elif name in kwargs:
                        kwargs[name] = func(kwargs[name])
                    else:
                        http_status(400, "Unknown Argument Name")
                        content_type("application/json")
                        return {"error": "'%s' was registered for type conversion but did not appear in the arguments list" % (name)}
            except ValueError as e:
                http_status(400, "Input Value Conversion Failed")
                content_type("application/json")
                return {"error": str(e)}

            # Unroll `pargs` into a list of arguments that are in the correct
            # order.
            pargs = []
            for name in argspec.args:
                try:
                    pargs.append(pargs_dict[name])
                except KeyError:
                    break

            # Call the wrapped function using the converted arguments.
            return f(*pargs, **kwargs)

        typed_func.typefuncs = typefuncs
        return typed_func
    return wrap