Lightweight in-memory cache with TTL, LRU eviction, and tag-based invalidation
pip install philiprehberger-cache-kitLightweight in-memory cache with TTL, LRU eviction, and tag-based invalidation.
pip install philiprehberger-cache-kit
from philiprehberger_cache_kit import Cache
cache: Cache[str] = Cache(max_size=1000)
cache.set("key", "value")
print(cache.get("key")) # "value"
cache = Cache(default_ttl=60.0) # 60 seconds default
cache.set("session", "abc123") # uses default TTL
cache.set("temp", "data", ttl=5.0) # expires in 5 seconds
cache.set("permanent", "data", ttl=None) # no expiry when default is set
cache = Cache(max_size=100)
# When full, least recently used entries are evicted first
# Expired entries are evicted before non-expired ones
for i in range(200):
cache.set(f"key-{i}", f"value-{i}")
print(cache.size) # 100
cache.set("user:1", user_data, tags={"users", "team-a"})
cache.set("user:2", user_data, tags={"users", "team-b"})
cache.set("post:1", post_data, tags={"posts", "team-a"})
# Invalidate all entries tagged "team-a"
removed = cache.invalidate_by_tag("team-a")
print(removed) # 2
from philiprehberger_cache_kit import Cache
cache: Cache[dict] = Cache(max_size=1000)
def fetch_user(user_id: int) -> dict:
# Expensive lookup (DB, HTTP, etc.) — only called on cache miss.
return {"id": user_id, "name": "Ada"}
# First call: computes, caches with TTL/tags, returns the value (miss).
user = cache.get_or_compute(
"user:1",
lambda: fetch_user(1),
ttl=60.0,
tags=["users"],
)
# Second call within TTL: returns cached value, compute_fn is NOT invoked (hit).
user = cache.get_or_compute("user:1", lambda: fetch_user(1))
# Set multiple entries at once
cache.set_many({"a": 1, "b": 2, "c": 3}, ttl=30.0)
# Get multiple entries at once (skips missing/expired)
results = cache.get_many(["a", "b", "missing"])
print(results) # {"a": 1, "b": 2}
from philiprehberger_cache_kit import Cache, CacheStats
cache: Cache[str] = Cache(max_size=100)
cache.set("x", "hello")
cache.get("x") # hit
cache.get("missing") # miss
stats = cache.stats()
print(stats.hits) # 1
print(stats.misses) # 1
print(stats.hit_rate) # 0.5
print(stats.evictions) # 0
print(stats.expired) # 0
cache.reset_stats() # zero out all counters
cache.has("key") # check existence
"key" in cache # same as has()
cache.delete("key") # delete single entry
cache.keys() # list all non-expired keys
len(cache) # count of non-expired entries
cache.get_entry("key") # get CacheEntry with tags, expires_at
cache.clear() # remove everything
| Function / Class | Description |
|---|---|
Cache(max_size=1000, default_ttl=None) | Create a new cache |
.set(key, value, ttl=None, tags=None) | Store a value |
.get(key, default=None) | Retrieve a value |
.get_or_compute(key, compute_fn, ttl=None, tags=None) | Return cached value, or compute and cache it on miss |
.get_many(keys) | Retrieve multiple values, skip missing/expired |
.set_many(items, ttl=None) | Store multiple values |
.has(key) | Check if key exists and is not expired |
.delete(key) | Remove a key |
.invalidate_by_tag(tag) | Remove all entries with a tag |
.clear() | Remove all entries |
.keys() | List non-expired keys |
.get_entry(key) | Get CacheEntry object (value, expires_at, tags) |
.stats() | Get CacheStats (hits, misses, evictions, expired, hit_rate) |
.reset_stats() | Zero out all stat counters |
.size | Number of stored entries |
len(cache) | Number of non-expired entries |
key in cache | Check key existence |
CacheStats | Dataclass with hits, misses, evictions, expired, hit_rate |
pip install -e .
python -m pytest tests/ -v
If you find this project useful: