Source code for tensiometer.utilities.caching
"""
This file contains utilities that are useful for caching
"""
###########################################################################################
# Initial imports:
import os
import pickle
from functools import wraps
###########################################################################################
[docs]
def cache_input(func, check_input=True):
"""
Cache a function's positional and keyword arguments on disk.
The decorator expects ``cache_dir`` and ``root_name`` keyword arguments on
the wrapped function. It persists the arguments to
``<cache_dir>/<root_name>_function_cache.plk`` and reloads them on
subsequent calls, optionally validating they match the current inputs.
:param func: function to decorate.
:param check_input: whether to verify cached inputs match current values.
:returns: wrapped function that reuses cached arguments when available.
:raises ValueError: if required cache kwargs are missing or inputs differ
from the cached values when ``check_input`` is True.
"""
@wraps(func)
def wrapper(*args, **kwargs):
# Retrieve caching essentials from function's keyword arguments
cache_dir = kwargs.get('cache_dir', None)
root_name = kwargs.get('root_name', None)
# If no cache_dir or root_name is provided, raise an error
if cache_dir is None or root_name is None:
raise ValueError('cache_dir and root_name must be provided for input to be cached')
# Ensure the cache directory exists
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
# Define the cache file path
cache_file = os.path.join(cache_dir, root_name + '_function_cache.plk')
# If the cache file exists, load the cached arguments
if os.path.exists(cache_file):
with open(cache_file, 'rb') as f:
cached_args, cached_kwargs = pickle.load(f)
# check whether the cached arguments are the same as the current ones:
if check_input:
if args != () and args != cached_args:
raise ValueError(
f"Cached arguments are different from current ones. "
f"args: {args}, cached_args: {cached_args}. "
f"Please remove the cache file and run the function again."
)
if kwargs != {}:
for key in kwargs:
if kwargs[key] != cached_kwargs[key]:
raise ValueError(
f"Cached arguments are different from current ones. "
f"Error in key: {key}. "
f"Provided: {kwargs[key]}, Cached: {cached_kwargs[key]}. "
f"Please remove the cache file and run the function again."
)
# if all checks pass, return the cached result:
args = cached_args
kwargs = cached_kwargs
else:
with open(cache_file, 'wb') as f:
pickle.dump((args, kwargs), f)
# Call the original function with the arguments
return func(*args, **kwargs)
return wrapper