Skip to main content

PyPI version Tests codecov License: GPLv3 Downloads

cachecache: Python function decorator for runtime-configurable caching. cachecache

A simple decorator to cache the results of your Python functions on disk and dynamically configure caching behavior at each function call, built on joblib.Memory.

from cachecache import cache

@cache
def my_function(arg1, arg2, arg3, ...):
    ...  # expensive computation

result = my_function("some/path", [1,2,3], 4)  # potentially slow the first time
result = my_function("some/path", [1,2,3], 4)  # same inputs -> instant from now on
result = my_function("some/path", [1,2,3], 4, again=True)  # recompute and overwrite cache, useful if data at some/path changed
result = my_function("some/path", [1,2,3], 4, cache_path="path/to/dir/with/space") # cache to a different location

Why cachecache over raw joblib?

We built cachecache to address joblib's Memory lack of user-friendliness, especially in interactive workflows:

  • It works across interactive sessions (e.g. Jupyter notebooks reloads). joblib caching tends to break on functions defined inside notebooks, because the cache directory gets redefined every time you restart the kernel. With cachecache, your cache persists across sessions.
  • Caching behavior is configurable at function call time. Every @cache-decorated function implicitly accepts extra arguments to alter how caching works, without any changes to the function signature:
    • again=True — recompute and overwrite a stale cache (useful when underlying data changed on disk, which the cache hash can't detect yet is very frequent in interactive data exploration)
    • cache_results=False — skip caching for a specific call (useful when the result would be too large to store)
    • cache_path="other/path" — cache to a different location (useful to distribute cache across disks)
  • Distributed caching. Cache a function's results next to the data it operates on (e.g. at datapath/.local_cache), so the cache lives where the data lives, rather than in a single global cache (likely to overfill). Joblib does not support this use case, which is very common in data analysis workflows.

Installation

Using uv (recommended):

uv pip install cachecache

Or from a local git clone:

git clone https://github.com/m-beau/cachecache.git
cd cachecache
uv pip install .

Using pip:

pip install cachecache

Usage

from cachecache import cache, Cacher

By default, results are cached in ~/.cachecache:

@cache # behind the scenes, "cache" is simply defined as "cache = Cacher()", which defaults to "~/.cachecache"
def my_cached_function(x, y):
    # complex operations...
    results = ...
    return results

result = my_cached_function(arg)  # potentially slow
result = my_cached_function(arg)  # always fast (results loaded from cache)

The caching control arguments again, cache_results, and cache_path are automatically injected to any decorated function — no need to declare them in the function signature:

result = my_cached_function(arg, again=True)               # recompute and overwrite cache
result = my_cached_function(arg, cache_results=False)       # skip caching entirely
result = my_cached_function(arg, cache_path="other/path")   # use a different cache directory

If you prefer, you can still declare them explicitly when you define the function (e.g. if you want to implement custom behavior depending on these arguments):

@cache
def my_cached_function(x, y, again=False, cache_results=True, cache_path=None):
    if again:
        print("Recomputing!")
    print(f"Caching path: {cache_path}")
    ...

Cache using a custom directory and maximum cache size:

cacher = Cacher("my/custom/caching/path", 10e9) # size in bytes - 10GB
@cacher
def my_cached_function(...):
    ...

When the cache exceeds its size limit, the least recently accessed items are evicted first. By default (when no limit is specified), cachecache allows caching up to all available disk space minus 1 GB, and will print a warning when less than 5 GB remain at the cache location.

Recompute results and overwrite cache:

result = my_cached_function(arg, again=True)

This proves useful if the results depend on data that can change on disk (this information is not present in the arguments of the function, so the cacher does not know about it!).

Adjust caching directory at runtime:

result = my_cached_function(arg, cache_path="somewhere/else")

This proves useful if you need to distribute the cached results of a function across several disks.

cachecache also provides a way to create a distributed_cacher that will cache a function's results at a location specified by a custom argument (such as 'datapath'):

from cachecache import Cacher, distributed_cacher

global_cacher = Cacher('~/.global_cache')

# Arguments of distributed_cacher:
# - datapath_arg_name (str, optional): The name of the argument in the decorated function
#     that specifies the datapath for the local cache. Defaults to 'datapath'.
# - local_cache_path (str, optional): The relative path to the local cache directory
#     within the datapath. Defaults to '.local_cache' (and results cached at f'{datapath}/.local_cache').
# - global_cache (cachecache.Cacher instance, optional): The global cacher to use by default
#     for cached functions without 'datapath_arg_name' (or when 'datapath_arg_name' is None).
#     Defaults to a cache at '~/.cachecache' (default instance of Cacher()).
dist_cacher = distributed_cacher(datapath_arg_name='datapath',
                                local_cache_path='.local_cache',
                                global_cache=global_cacher)

# You can then decorate a function as follow:
@dist_cacher
def my_distributed_cached_function(datapath, ...):
    """
    A function whose results will be cached at 'datapath/.local_cache'
    unless specified otherwise with the cache_path argument.

    Note: works with args and kwargs
    """
    ...

Behind the scenes, this works by swapping in the value of the specified argument (datapath_arg_name) instead of the 'cache_path' argument from Cacher (if 'cache_path' is also specified, it takes precedence over 'datapath').

Of course, you can use a single cacher for multiple functions:

@cacher
def foo1(x):
    return x ** 2

@cacher
def foo2(x):
    return x / 10

And both of these syntaxes are possible:

cacher = Cacher("my/custom/caching/path")
@cacher
def my_cached_function(...):
    ...

@Cacher("my/custom/caching/path")
def my_cached_function(...):
    ...

License

This project is licensed under the terms of the GNU General Public License v3.0. You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build & install instructions.

Support

If you have any questions, issues, or feature requests, please open an issue so that everybody can benefit from your experience! This package is actively maintained by Maxime Beau.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cachecache-1.0.1.tar.gz (150.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cachecache-1.0.1-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file cachecache-1.0.1.tar.gz.

File metadata

  • Download URL: cachecache-1.0.1.tar.gz
  • Upload date:
  • Size: 150.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cachecache-1.0.1.tar.gz
Algorithm Hash digest
SHA256 87b373b97c9a3f58efcd67820c591b69f5bc0d1746141736235b3e9b78fff2b4
MD5 d7c5cdc2cd75238b298e62f930c7d8c4
BLAKE2b-256 73ea15a59f253830ea564d8c8a83bb0600e1c0d35f103c140a177a8861c9995a

See more details on using hashes here.

File details

Details for the file cachecache-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: cachecache-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 22.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cachecache-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 85630c62811c9821999633d4b98be199b69ef191dff5e8b3bef15cbd94bfaccb
MD5 8fc233aa7d4a8a18f2f0d10ef1aaaf99
BLAKE2b-256 ef33e3729b5700963289fcf0abdffededfbc0cbdffed4812ab37fc858c21eb63

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

1.0.0

2 files

0.3.0

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page