Create a decorator for validating function parameters.
    
    Parameters
    ----------
    checkers_args: positional args
        Functions to apply to the inputs of the decorated function. The position of the argument 
        is assumed to match the position of the function in the decorator.
    checkers_kws: keyword args
        Keyword pairs in the form (arg: function) to apply to the inputs of the decorated function.

    Example
    -------    
    @accepts(df=df_checker)
    def do_something_with_df(df, args*, kw**):
        print(df.head())

def accepts(*checkers_args, **checkers_kws):
    """ Create a decorator for validating function parameters.
    
    Parameters
    ----------
    checkers_args: positional args
        Functions to apply to the inputs of the decorated function. The position of the argument 
        is assumed to match the position of the function in the decorator.
    checkers_kws: keyword args
        Keyword pairs in the form (arg: function) to apply to the inputs of the decorated function.

    Example
    -------    
    @accepts(df=df_checker)
    def do_something_with_df(df, args*, kw**):
        print(df.head())
    """
    
    @decorator
    def run_checkers(func, *args, **kwargs):
        all_args = inspect.getcallargs(func, *args, **kwargs)
        
        if checkers_args:
            for idx, checker_function in enumerate(checkers_args):
                if callable(checker_function):
                    result = checker_function(args[idx])
        if checkers_kws:
            for key in checkers_kws.keys():
                if key not in all_args:
                    raise ValueError('Argument specified in @accepts is not found in decorated function')
                else:
                    df = all_args[key]
                    result = checkers_kws[key](df)
        return func(*args, **kwargs)
    return run_checkers