Metadata-Version: 2.4
Name: kv-dict
Version: 0.2.0
Summary: KV backed dictionary, similar to RedisJSONDict
Author-email: Niko Kivel <niko.kivel@lightsource.ca>
License: GPL-3.0-only
License-File: LICENSE
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown

[![CI](https://github.com/Canadian-Light-Source/kv-dict/actions/workflows/ci.yml/badge.svg)](https://github.com/Canadian-Light-Source/kv-dict/actions/workflows/ci.yml)

# kv-dict

KV backed dictionary, similar to RedisJSONDict

## Install Package

- pip: `pip install kv-dict`
- uv: `uv add kv-dict`
  - add `--optional FEATURE` to add as an optional dependency
- pixi: `pixi add --pypi kv-dict`
  - add `--feature FEATURE` to add as an optional dependency

## Development

This project uses `uv` for managing project dependencies. Installation
instructions may be found
[here](https://docs.astral.sh/uv/getting-started/installation/).

```bash
git clone https://github.com/Canadian-Light-Source/kv-dict.git
cd kv-dict
uv sync --all-extras
```

## Remote Mapping + In-Memory Backend Example

Run a basic `RemoteKVMapping` flow backed by `InMemoryAsyncBackend`:

```bash
uv run python examples/remote_mapping_in_memory_example.py
```

## Remote Mapping + Redis/Dragonfly Backend Example

Run a basic `RemoteKVMapping` flow backed by a Redis-compatible server:

```bash
uv run python examples/remote_mapping_redis_example.py
```

> [!NOTE]
> In the devcontainer compose setup, the Redis-compatible service
> hostname is `redis`.

## Remote Mapping + PostgreSQL Backend Example

Run a basic `RemoteKVMapping` flow backed by PostgreSQL:

```bash
uv run python examples/remote_mapping_postgres_example.py
```

> [!NOTE]
> In the devcontainer compose setup, PostgreSQL is available on the
> internal hostname/port `postgres:5432`.

## Remote Mapping + NATS JetStream KV Backend Example

Run a basic `RemoteKVMapping` flow backed by NATS JetStream KV:

```bash
uv run python examples/remote_mapping_nats_example.py
```

> [!IMPORTANT]
> This example uses `create_bucket=False` (production-style).
> Ensure the `kv_dict` bucket already exists.

> [!NOTE]
> In the devcontainer compose setup, NATS is also available to host-side
> tools at `nats://127.0.0.1:14222`.

Watch the changes in the NATS KV with the CLI client:

```bash
nats -s localhost:14222 kv watch kv_dict
```

<details>
<summary>Create the <code>kv_dict</code> bucket locally (for testing)</summary>

Run this once before executing the NATS example:

```bash
uv run python - <<'PY'
import asyncio
import nats


async def main() -> None:
  nc = await nats.connect(servers=["nats://nats:4222"])
  try:
    js = nc.jetstream()
    try:
      await js.key_value("kv_dict")
      print("Bucket already exists: kv_dict")
    except Exception:
      await js.create_key_value(bucket="kv_dict")
      print("Created bucket: kv_dict")
  finally:
    await nc.close()


asyncio.run(main())
PY
```

</details>

## Differences from Python `dict`

`RemoteKVMapping` is intentionally `dict`-like, but not a byte-for-byte
replacement of the built-in `dict` behavior.

### Key behavioral differences

- Iteration order is sorted by key, not insertion order.
- Values are persisted through backend round-trips, so operations are not purely
  in-memory.
- Nested `dict` and `list` mutations are write-through.
- Mutable non-dict/non-list values are not automatically write-through.

### Missing / non-parity APIs

- `fromkeys()` is not implemented.

### Practical guidance

> [!WARNING]
> `RemoteKVMapping` is backend-backed, not purely in-memory. Avoid
> relying on insertion-order behavior and reassignment-free updates for mutable
> non-list, non-dict nested values.

- Treat this mapping as a backend-backed structure, not an in-memory object.
- For nested non-dict updates, reassign the modified value to persist changes.
- If strict insertion-order semantics are required, do not rely on iteration
  order from `RemoteKVMapping`.

### Dict parity roadmap

- [x] Implement `copy()` semantics for backend-backed snapshots.
- [x] Implement in-place dict union operator (`|=`).
- [x] Implement dict union operator (`|`).
- [ ] Implement `fromkeys()` with explicit persistence semantics.
- [x] Add write-through wrappers for list operations.
- [ ] Add write-through wrappers for additional mutable container types (if
      required).
- [ ] Decide and document stable ordering strategy (sorted vs insertion-order).
- [ ] Add dedicated parity tests against a reference `dict` behavior matrix.
