Skip to main content

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, and list_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 default if 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 str arguments and str return values
  • For complex types, use CacheClient directly 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 aiosqlite wrapper if needed)

Development

This package is auto-generated from a schema definition using a custom build pipeline:

  1. Schema definition (Jsonnet) → JSON Schema
  2. JSON Schema → SQL DDL
  3. JSON Schema → YeSQL query templates
  4. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

squeakyv-0.1.1.tar.gz (10.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

squeakyv-0.1.1-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

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

Hashes for squeakyv-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e2c5c672b226bb8330bdb15b12475172cec8671a84797a0e3d496f4d0eb0116a
MD5 07b5c07aba365ac2d536e9187aa6ca16
BLAKE2b-256 c934a6b8d4e2d5852aadc30a980036172adbc6eff02c691e31fafadfeac0bd7f

See more details on using hashes here.

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

Hashes for squeakyv-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9d2ff7d640f9a498ea0b681d73c6d249dbc073355db4fb937f763af91d9dd9b
MD5 a49da2981fa0869b12cb893df4ca1991
BLAKE2b-256 58ba943209864e891ea8d6531b73b6f9ca951b5df88d9b1cdfb9ecea4f3d4e67

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page