Skip to main content

ConditionalCache

ConditionalCache

ConditionalCache is a set of decorators, that provide conditional function memoization and selective cache clearing.

It works under the same interface that most standard cache decorators like functools.lru_cache or cachetools.ttl_cache, but unlocking a new condition parameter, that will determine if the function result is memoized or not. This feature allows for more granular control over caching behavior, useful for those use cases where we want to store the output only when certain conditions are met. As for example when checking existence in databases.

Installation

To install ConditionalCache simply run:

pip install conditional-cache

Usage

Working with ConditionalCache is as simple and straight-forward as using functools.lru_cache, as it works under the same interface.

from conditional_cache import lru_cache

# Memoize the returned element only when it is different than "Not Found"
@lru_cache(maxsize=64, condition=lambda db_value: db_value != "Not Found")
def element_exists_in_db(element_id: int) -> str:

  print(f"Asked to DB: {element_id}")
  # For the example let's consider that even elements exists.
  return "Found" if element_id % 2 == 0 else "Not Found"

When we will call this function, it will be execute only once for even numbers, and always for odds.

# Will be executed, and not memoized
print(f"Returned: {element_exists_in_db(element_id=1)}")
# Will be executed again
print(f"Returned: {element_exists_in_db(element_id=1)}\n")

# Will be executed and memoized
print(f"Returned: {element_exists_in_db(element_id=2)}")
# Will return the memoized result without executing again
print(f"Returned: {element_exists_in_db(element_id=2)}")
>> Asked to DB: 1
>> Returned: Not Found
>> Asked to DB: 1
>> Returned: Not Found

>> Asked to DB: 2
>> Returned: Found
>> Returned: Found

If during your execution, you perform an action that invalidate a given function result, you can actively remove that element cache:

# Will return the result that was memoized before
print(f"Returned: {element_exists_in_db(element_id=2)}\n")
# Remove the element from the cache
element_exists_in_db.cache_remove(element_id=2)

# Will be executed again and memoized
print(f"Returned: {element_exists_in_db(element_id=2)}")
# Will return the memoized result
print(f"Returned: {element_exists_in_db(element_id=2)}")
>> Returned: Found

>> Asked to DB: 2
>> Returned: Found
>> Returned: Found

Controlling cache size by memory

In addition to maxsize (number of elements), you can also limit the cache by memory usage with maxsize_bytes.

from conditional_cache import lru_cache

@lru_cache(maxsize_bytes=1024)  # keep up to ~1 KB of cached data
def heavy_query(x: int) -> str:
    print("Executed:", x)
    return "X" * (x * 100)

heavy_query(1)   # Cached
heavy_query(10)  # May evict older entries if too large

This way you can avoid overflowing your memory if you need to cache large objects like images. If a single result is too large to ever fit in the cache, it will just not be stored.

Time-based expiration (TTL)

Use ttl_cache when you want cached entries to automatically expire after a given number of seconds.

import time
from conditional_cache import ttl_cache

@ttl_cache(ttl=3, maxsize=64, condition=lambda r: r is not None)
def fetch_user(user_id: int) -> dict | None:
    print("Fetching:", user_id)
    return {"id": user_id}

fetch_user(1)      # Executed and cached
time.sleep(1)
fetch_user(1)      # Retrieved from cache
time.sleep(3)
fetch_user(1)      # Expired -> executed again

Unhashable arguments

Unlike functools, ConditionalCache supports common unhashable types like list, dict, or set as arguments. They are transparently converted to hashable equivalents to avoid headaches.

from conditional_cache import lru_cache

@lru_cache(maxsize=32)
def stringify(a: list, b: dict) -> str:
    print("Executed:", a, b)
    return str(a) + str(b)

print(stringify([1,2,3], {"x": 42}))
print(stringify([1,2,3], {"x": 42}))  # retrieved from cache

API Reference

conditional_cache.lru_cache(maxsize: int = 128, maxsize_bytes: int | None = None, typed: bool = False, condition: callable = lambda x: True)

An Least Recently Used Cache. It works the same way that functools.lru_cache but accepting conditional storage and selective item removing through <decorated_function>.cache_remove(**args)

  • maxsize: int. The maximum amount of elements to keep cached. Once the cache is full, new elements will start to override oldest ones.
  • maxsize_bytes: int | None. The maximum amount of memory (in bytes, as estimated by sys.getsizeof) to keep cached. Useful when caching large objects. If a single item is larger than this budget, it will simply not be cached.
  • typed: bool. Works the same way that functools.lru_cache. If True, function arguments of different types will be cached separately.
  • condition: callable. It must be a function that receives a single parameter as input (the output of the decorated method) and returns a boolean. True if the result should be cached or False if it should not.

conditional_cache.ttl_cache(maxsize: int = 128, maxsize_bytes: int | None = None, typed: bool = False, ttl: int = 60, condition: callable = lambda x: True)

A Time-To-Live cache. Behaves like lru_cache with the same conditional storage and selective removal features, but cached entries automatically expire after ttl seconds.

  • maxsize: int. Maximum number of elements to keep cached.
  • maxsize_bytes: int | None. Maximum memory budget for cached data. Items larger than this budget are not cached.
  • typed: bool. If True, function arguments of different types are cached separately.
  • ttl: int. Time-to-live in seconds for each cached entry.
  • condition: callable. Receives the function output and returns True if it should be cached.

Metadata

Release files for conditional-cache 1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for conditional-cache 1.5
File Size Uploaded
conditional_cache-1.5.tar.gz 5.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for conditional-cache 1.5
File Interpreter ABI Platform
conditional_cache-1.5-py3-none-any.whl Python 3 none any Details

Total release size: 12.0 kB

Release files / conditional_cache-1.5.tar.gz

Download URL conditional_cache-1.5.tar.gz
Size 5.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f0aaffd4af2eb1c078c65c0a7a07eef3ce5cc3a1705a803f2d99f44171c8db6c
BLAKE2b-256 checksum
How to use checksums
9bbee0bff5239af1a8106d986d4be2c9be4046b9d814bd8e73bac72802194f64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / conditional_cache-1.5-py3-none-any.whl

Download URL conditional_cache-1.5-py3-none-any.whl
Size 6.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a531ed1d51e7ad027b84c0c73e6f8646c224c5b715681fdb98f404e324121ff3
BLAKE2b-256 checksum
How to use checksums
c1f0459b4c1536bd190d7aa32e12239bfa20bb1a6f835a358622d505604ab8a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release history Release notifications | RSS feed

This release

1.5 This release

2 release files

1.4

2 release files

1.3

2 release files

1.2

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page