def METHOD_NAME(fun: Callable[..., T]) -> Callable[..., T]:
    def newfun(*args: Any, **kwargs: Any) -> T:
        global callers_local_vars
        sig = signature(fun)
        if sig.return_annotation is not pd.DataFrame:
            raise TypeError(
                "The wrapped function must have a return type of "
                "pandas DataFrame and be annotated as so."
            )
        bound_args = sig.bind(*args, **kwargs)
        all_args = {
            **dict(
                (param.name, param.default)
                for param in sig.parameters.values()
            ),
            **dict(bound_args.arguments.items()),
        }
        l_vars = currentframe().f_back.f_locals.items()  # type: ignore
        args_ = list()
        for value in all_args.values():
            if isinstance(value, pd.DataFrame) or (
                hasattr(value, "data")
                and isinstance(value.data, pd.DataFrame)
            ):
                attempt = None
                if pd_varnames:
                    attempt = next(
                        (
                            var_name
                            for var_name, var_val in l_vars
                            if var_val is value
                        ),
                        None,
                    )
                if attempt is not None:
                    args_.append(attempt)
                else:
                    args_.append(md5(value.values.tobytes()).hexdigest())
            else:
                args_.append(f"{value}")
        filepath = cache_directory / (
            fun.__name__ + "_" + "_".join(args_) + ".pkl"
        )
        if filepath.exists():
            print(f"Reading cached data from {filepath}")
            return loader(filepath)
        res = fun(*args, **kwargs)
        res.to_pickle(filepath)
        return res
    return newfun