Source code for alchemiq.cache.memory

"""In-process cache backend backed by a plain dict."""

from __future__ import annotations

import fnmatch
import time


[docs] class InMemoryCache: """Pure-Python :class:`.CacheBackend` for tests and small single-process apps. Backed by a plain ``dict``; no external dependencies. TTL is enforced lazily on ``get``. Counter keys (used by the version-invalidation scheme) never expire. E.g.:: from alchemiq.cache import InMemoryCache, configure_cache configure_cache(backend=InMemoryCache(default_ttl=120, namespace="myapp")) .. note:: Not safe for use across multiple processes or workers - use :func:`.configure_cache` with a Redis ``url`` for multi-process deployments. """ def __init__(self, *, default_ttl: int = 60, namespace: str = "aq") -> None: self.default_ttl = default_ttl self.namespace = namespace self._store: dict[str, tuple[str, float | None]] = {}
[docs] async def get(self, key: str) -> str | None: """Return the cached value, or ``None`` on miss or TTL expiry (lazy eviction).""" item = self._store.get(key) if item is None: return None value, expires_at = item if expires_at is not None and time.monotonic() >= expires_at: self._store.pop(key, None) return None return value
[docs] async def set(self, key: str, value: str, *, ttl: int) -> None: """Store ``value`` under ``key``; a zero TTL means no expiry.""" expires_at = time.monotonic() + ttl if ttl else None self._store[key] = (value, expires_at)
[docs] async def delete(self, *keys: str) -> None: """Remove one or more keys; silently ignores missing keys.""" for key in keys: self._store.pop(key, None)
[docs] async def incr(self, key: str) -> int: """Increment an integer counter, starting at 1 for a new key; never expires.""" current = await self.get(key) nxt = (int(current) + 1) if current is not None else 1 self._store[key] = (str(nxt), None) # version keys never expire return nxt
[docs] async def scan_delete(self, match: str) -> None: """Delete all keys matching a glob pattern.""" for key in [k for k in self._store if fnmatch.fnmatch(k, match)]: self._store.pop(key, None)