Metadata-Version: 2.4
Name: kv-cache
Version: 0.3.4
Summary: A fast key-value store using SQLite for CLI tools
Project-URL: Homepage, https://github.com/yourusername/fast-kv
Project-URL: Repository, https://github.com/yourusername/fast-kv.git
Project-URL: Documentation, https://github.com/yourusername/fast-kv#readme
Project-URL: Bug Tracker, https://github.com/yourusername/fast-kv/issues
Author-email: Your Name <your.email@example.com>
License-Expression: MIT
License-File: LICENSE
Keywords: cache,cli,key-value,sqlite
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Provides-Extra: dev
Requires-Dist: black>=22.0; extra == 'dev'
Requires-Dist: isort>=5.0; extra == 'dev'
Requires-Dist: mypy>=0.981; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
Requires-Dist: pytest-cov>=3.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# 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

```bash
pip install kv-cache
```

## Quick Start

### Basic Key-Value Operations

```python
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

```python
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.

```python
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.

```python
@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:

```python
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:

```python
@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

```python
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`

```python
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`

```python
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)` and `f(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

```bash
# 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](LICENSE) for details.
