kv-cache
A fast, persistent key-value store using SQLite as a backend. Zero dependencies beyond the Python standard library. Designed for CLI tools needing persistent cache storage.
Features
- SQLite-based persistent storage with WAL mode for performance
- Built-in TTL (Time-to-Live) support with automatic cleanup
- Thread-safe operations via thread-local connections
- Function caching decorator (
scache) supporting both sync and async functions - Deterministic cache key generation using SHA-256
- Context manager support
Installation
pip install kv-cache
Quick Start
Basic Key-Value Operations
from kv_cache import KVStore
# Initialize the store
store = KVStore("cache.db")
# Store a value
store.set("user:1", {"name": "Alice", "age": 30})
# Store a value with a 1-hour TTL
store.set("session:abc", {"token": "xyz"}, ttl=3600)
# Retrieve values
user = store.get("user:1") # {'name': 'Alice', 'age': 30}
missing = store.get("no_key", default=0) # 0
# Delete a key
store.delete("user:1")
# List all keys, values, or items
store.keys() # ['session:abc']
store.values() # [{'token': 'xyz'}]
store.items() # [('session:abc', {'token': 'xyz'})]
# Clear all entries
store.clear()
# Close the connection
store.close()
Context Manager
with KVStore("cache.db") as store:
store.set("key", "value", ttl=60)
print(store.get("key")) # "value"
# Connection is automatically closed
Function Caching with scache
The scache decorator caches function return values. Cache keys are automatically generated from the function name and arguments.
import time
from kv_cache import KVStore, scache
store = KVStore("~/.myapp/cache.db")
@scache(ttl=3600, store=store)
def fetch_data(user_id, include_details=False):
time.sleep(2) # Simulate slow API call
return {"user_id": user_id, "details": include_details}
fetch_data(42, include_details=True) # Takes 2 seconds
fetch_data(42, include_details=True) # Instant (cached)
fetch_data(42, include_details=False) # Takes 2 seconds (different args = different key)
Async Function Caching
scache automatically detects async functions and wraps them accordingly.
@scache(ttl=300, store=store)
async def fetch_remote(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.json()
Custom Cache Key Function
Override the default key generation with a custom function:
def my_key_func(func, args, kwargs):
# Only cache based on the first argument
return f"{func.__name__}:{args[0]}"
@scache(ttl=600, store=store, key_func=my_key_func)
def search(query, page=1):
return api.search(query, page)
Conditional Caching
Disable caching dynamically by setting a flag in the store:
@scache(ttl=3600, store=store, conditional_key="enable_cache")
def get_config():
return load_config_from_disk()
# Caching is active by default
get_config() # cached
# Disable caching by setting the conditional key to False
store.set("enable_cache", False)
get_config() # always calls load_config_from_disk()
CLI Autocomplete Example
from kv_cache import KVStore
def get_completions(prefix: str) -> list:
store = KVStore("~/.mycli/cache.db")
cache_key = f"auto:{prefix}"
results = store.get(cache_key)
if results is None:
results = fetch_from_remote_server(prefix)
store.set(cache_key, results, ttl=3600)
return results
API Reference
KVStore
KVStore(db_path: str, table_name: str = "key_value_store")
A persistent key-value store backed by SQLite.
| Parameter | Type | Default | Description |
|---|---|---|---|
db_path |
str |
(required) | Path to the SQLite database file. Parent directories are created automatically. |
table_name |
str |
"key_value_store" |
Name of the SQLite table. Allows multiple logical stores in one database. |
Methods
| Method | Signature | Description |
|---|---|---|
set |
set(key: str, value: Any, ttl: Optional[int] = None) |
Store a JSON-serializable value. Optional ttl in seconds. |
get |
get(key: str, default: Any = None) -> Any |
Retrieve a value. Returns default if the key is missing or expired. |
delete |
delete(key: str) |
Remove a key from the store. |
keys |
keys() -> list |
Return all keys (including expired ones pending cleanup). |
values |
values() -> list |
Return all deserialized values. |
items |
items() -> list |
Return all (key, value) tuples. |
clear |
clear() |
Delete all entries from the store. |
close |
close() |
Close the database connection. |
Context manager: KVStore supports with statements. The connection is closed on exit.
Thread safety: Each thread gets its own SQLite connection via threading.local().
Values: Any JSON-serializable Python object (dicts, lists, strings, numbers, booleans, None).
scache
scache(
ttl: Optional[int] = None,
store: Optional[KVStore] = None,
conditional_key: str = '',
key_func: Optional[Callable] = None
) -> Callable
Decorator that caches function return values in a KVStore.
| Parameter | Type | Default | Description |
|---|---|---|---|
ttl |
Optional[int] |
None |
Cache lifetime in seconds. None means no expiration. |
store |
Optional[KVStore] |
None |
KVStore instance to use. If None, creates a default store at cache.db. |
conditional_key |
str |
'' |
A key in the store that controls whether caching is active. If the key's value is False, caching is bypassed. |
key_func |
Optional[Callable] |
None |
Custom key generation function with signature (func, args, kwargs) -> str. |
Key generation: By default, the cache key is a SHA-256 hash of the function name, serialized positional arguments, and sorted keyword arguments. This means:
- Keyword argument order doesn't matter:
f(a=1, b=2)andf(b=2, a=1)hit the same cache entry. - Different argument values always produce different keys.
- Non-JSON-serializable objects fall back to their
str()representation.
Return value caveat: Since None is used as the cache-miss sentinel, functions that return None will not be cached (the decorator will always re-execute them).
SQLite Configuration
The store uses the following SQLite pragmas for performance:
| Pragma | Value | Purpose |
|---|---|---|
journal_mode |
WAL | Write-Ahead Logging for concurrent reads |
synchronous |
NORMAL | Balance between safety and speed |
mmap_size |
512 MB | Memory-mapped I/O |
cache_size |
128 MB | In-memory page cache |
page_size |
4 KB | Database page size |
Development
# Clone and install
git clone https://github.com/lcances/kv_cache.git
cd kv_cache
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
# Run tests
pytest tests/
# Format code
black src/ tests/
isort src/ tests/
Requirements
- Python >= 3.7
- No external dependencies (stdlib only)
License
MIT License - see LICENSE for details.
Release files for kv-cache 0.3.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kv_cache-0.3.4.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kv_cache-0.3.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.5 kB
Release files / kv_cache-0.3.4.tar.gz
| Download URL | kv_cache-0.3.4.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d652a5d7ae451bdc1fca415fe348a645bf595ed6293fe995f084a3c27b51187f
|
|
BLAKE2b-256 checksum How to use checksums |
c0b2a7cc1fd031d42d3c88036a7554f84d76603559724df54b9f9fde87df161f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.
Transparency logRelease files / kv_cache-0.3.4-py3-none-any.whl
| Download URL | kv_cache-0.3.4-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8f11148ce135047d830089bc8419d6c6abc87b3c1add3ae02a9026011f709a75
|
|
BLAKE2b-256 checksum How to use checksums |
695e81363ae588c2d1bb8887a56440eb3063ee0a1e38486dd71d8f157f166545
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.
Transparency log