Skip to main content

Simple SQLite-backed key-value caching library

Reason this release was yanked:

wrong username in pyproject

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.0.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.0-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file squeakyv-0.1.0.tar.gz.

File metadata

  • Download URL: squeakyv-0.1.0.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.0.tar.gz
Algorithm Hash digest
SHA256 e09c29bd18f5633ce165c486ab8f3e38d52bdcece395ec5394909b32f2c9f57d
MD5 f4838b3fa0a68f1f4c4b6b83a3b20e07
BLAKE2b-256 f1a7dc4a58eb0c16329aaa98f029d0facca22d6f977dc01bcf26f9bb39b287b8

See more details on using hashes here.

File details

Details for the file squeakyv-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: squeakyv-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9129c5237fcab8838f93b719f7fdb208bd89485b351b564a4e9a679c483cf353
MD5 f51e6fdbced904f0b082d4883f09e853
BLAKE2b-256 e4aa4a968465270e5fc21af94aada09699166e4e79928e282313b178b5b23dc6

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