Given a function, return a tuple of
    (positional_params, keyword_params, varargs, defaults, annotations)
    where
    * positional_params is a list of parameters that don't have default values
    * keyword_params is a list of parameters that have default values
    * varargs is the string name for variable arguments
    * defaults is a dict of default values for the keyword parameters
    * annotations is a dictionary of param_name: annotation pairs
        it may be empty, and on python 2 will always be empty.

    This function is portable between Python 2 and Python 3, and does some
    extra processing of the output from inspect.

def portable_argspec(func):
    '''
    Given a function, return a tuple of
    (positional_params, keyword_params, varargs, defaults, annotations)
    where
    * positional_params is a list of parameters that don't have default values
    * keyword_params is a list of parameters that have default values
    * varargs is the string name for variable arguments
    * defaults is a dict of default values for the keyword parameters
    * annotations is a dictionary of param_name: annotation pairs
        it may be empty, and on python 2 will always be empty.

    This function is portable between Python 2 and Python 3, and does some
    extra processing of the output from inspect.
    '''
    if sys.version_info < (3, 0):  # PYTHON 2 MUST DIE
        argnames, varargs, varkw, defaults = inspect.getargspec(func)
        annotations = {}
    else:
        (
            argnames, varargs, varkw, defaults, kwa, kwd, annotations
        ) = inspect.getfullargspec(func)

    kw_boundary = len(argnames) - len(defaults) if defaults else len(argnames)
    positional_params = argnames[:kw_boundary]
    kw_params = argnames[kw_boundary:]
    return positional_params, kw_params, varargs, defaults, annotations