Simple SQLite-backed key-value caching library
Project description
squeakyv
A simple, SQLite-backed key-value caching library designed for simplicity and cross-language portability.
Features
- Simple API: Just
get,set,delete, andlist_keys - Thread-safe: Uses thread-local connections for safe concurrent access
- Zero dependencies: Pure stdlib, only requires Python 3.9+
- Version history: Soft deletes preserve value history
- Memoization: Built-in decorator for caching function results
- In-memory or persistent: Use
:memory:for ephemeral cache or file path for persistence
Installation
pip install squeakyv
Quick Start
Basic Usage
from squeakyv import CacheClient
# Create a cache (in-memory by default)
cache = CacheClient()
# Store a value
cache.set("mykey", b"Hello, World!")
# Retrieve a value
value = cache.get("mykey")
print(value) # b'Hello, World!'
# List all keys
keys = cache.list_keys()
print(keys) # ['mykey']
# Delete a key
cache.delete("mykey")
# Get with default for missing keys
value = cache.get("nonexistent", default=b"default")
print(value) # b'default'
Persistent Cache
from squeakyv import CacheClient
# Use a file path for persistent storage
cache = CacheClient("my_cache.db")
cache.set("key", b"value")
# Data persists across restarts
cache2 = CacheClient("my_cache.db")
print(cache2.get("key")) # b'value'
Context Manager
from squeakyv import CacheClient
with CacheClient("my_cache.db") as cache:
cache.set("key", b"value")
value = cache.get("key")
# Connection automatically closed
Function Memoization
from squeakyv import memoize
import os
# Set default cache location (optional)
os.environ["SQUEAKYV_DATABASE"] = "cache.db"
@memoize()
def expensive_function(arg: str) -> str:
print(f"Computing result for {arg}")
return f"result for {arg}"
# First call executes function
result = expensive_function("test") # Prints: Computing result for test
# Second call returns cached result
result = expensive_function("test") # No print - returned from cache
API Reference
CacheClient
Constructor
CacheClient(path: str = ":memory:")
path: Database file path. Defaults to":memory:"for in-memory cache.
Methods
-
get(key: str, default: Any = None) -> bytes | None- Retrieve value for a key
- Returns
defaultif key doesn't exist
-
set(key: str, value: bytes) -> None- Store a value for a key
- Value must be bytes (use
.encode()for strings) - Creates new version if key exists (old value soft-deleted)
-
delete(key: str) -> None- Delete a key (soft delete - preserves history)
-
list_keys() -> list[str]- List all active keys
- Ordered by insertion time (newest first)
-
close() -> None- Close thread-local database connection
Context Manager
with CacheClient("db.db") as cache:
cache.set("key", b"value")
memoize
Decorator
@memoize(key_prefix: Optional[str] = None)
def function(arg: str) -> str:
...
key_prefix: Optional cache key prefix (defaults to function's qualified name)- Limitation: Only supports
strarguments andstrreturn values - For complex types, use
CacheClientdirectly with custom serialization
KeyNotFoundError
Exception raised when accessing non-existent keys (currently not used, reserved for future strict mode).
Thread Safety
CacheClient is thread-safe through thread-local connections. Each thread gets its own SQLite connection, avoiding concurrency issues.
from threading import Thread
from squeakyv import CacheClient
cache = CacheClient("shared.db")
def worker(i):
cache.set(f"key{i}", f"value{i}".encode())
print(cache.get(f"key{i}"))
threads = [Thread(target=worker, args=(i,)) for i in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
Data Storage
Values are stored as raw bytes. For structured data:
import json
import pickle
# JSON (strings only)
cache.set("data", json.dumps({"key": "value"}).encode())
data = json.loads(cache.get("data").decode())
# Pickle (any Python object)
cache.set("object", pickle.dumps({"complex": [1, 2, 3]}))
obj = pickle.loads(cache.get("object"))
Version History
squeakyv uses soft deletes, preserving value history. When you update a key, the old value is marked inactive but remains in the database. This enables:
- Audit trails
- Time-travel queries (future feature)
- Rollback capabilities (future feature)
Current API only exposes active values. Historical queries will be added in future versions.
Cross-Language Design
squeakyv is designed for consistent semantics across multiple languages. The Python implementation uses code generation from a shared schema definition, ensuring identical behavior when implemented in Go, Ruby, Clojure, Bash, etc.
Core principles:
- Minimal features that work everywhere
- Raw bytes storage (no language-specific serialization)
- Simple, stateless operations
- SQLite as the universal backend
Performance Notes
- Thread-local connections minimize locking overhead
- Autocommit mode (
isolation_level=None) for faster writes - Indexes on
(key, is_active)for efficient lookups - In-memory mode (
:memory:) for maximum performance
Limitations
- Values must be bytes (no automatic serialization)
- No TTL/expiration support (keep design simple)
- No namespacing (single flat keyspace)
- Memoize decorator limited to string args/returns
- No async support (use
aiosqlitewrapper if needed)
Development
This package is auto-generated from a schema definition using a custom build pipeline:
- Schema definition (Jsonnet) → JSON Schema
- JSON Schema → SQL DDL
- JSON Schema → YeSQL query templates
- YeSQL → Python code (via language-specific renderer)
To rebuild:
# Run the build pipeline (requires sdflow)
sdflow generate-python-target
License
MIT License - see LICENSE file for details
Contributing
Contributions welcome! This is a simple library by design - please keep PRs focused on:
- Bug fixes
- Performance improvements
- Documentation improvements
- Cross-language consistency
Avoid adding language-specific features that can't be replicated across all target languages.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file squeakyv-0.1.1.tar.gz.
File metadata
- Download URL: squeakyv-0.1.1.tar.gz
- Upload date:
- Size: 10.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2c5c672b226bb8330bdb15b12475172cec8671a84797a0e3d496f4d0eb0116a
|
|
| MD5 |
07b5c07aba365ac2d536e9187aa6ca16
|
|
| BLAKE2b-256 |
c934a6b8d4e2d5852aadc30a980036172adbc6eff02c691e31fafadfeac0bd7f
|
File details
Details for the file squeakyv-0.1.1-py3-none-any.whl.
File metadata
- Download URL: squeakyv-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9d2ff7d640f9a498ea0b681d73c6d249dbc073355db4fb937f763af91d9dd9b
|
|
| MD5 |
a49da2981fa0869b12cb893df4ca1991
|
|
| BLAKE2b-256 |
58ba943209864e891ea8d6531b73b6f9ca951b5df88d9b1cdfb9ecea4f3d4e67
|