Skip to main content

virtuals

Native Python dicts, lists and sets that live in a KV store.

PyPI - Python Version PyPI Package PyPI Downloads License

About

Dicts, lists, sets and ordered collections that look and feel like native Python, but sit in a KV store. Each one is a lazy view over an ordered key-value store: reads stream in from disk on access, writes land as rows, and a nested dict of a billion entries costs nothing until you touch it.

Current backends: SQLite, LMDB, RocksDB, in-memory.

Notificaitons and observabiltiy, serializable transactions, real snapshots.

pip install virtuals-py
pip install "virtuals-py[lmdb]"     # LMDB backend
pip install "virtuals-py[rocksdb]"  # RocksDB backend
pip install "virtuals-py[redis]"    # cross-process notifications

A quick look

from virtuals import Navigator
from virtuals.codecs import BinaryCodec
from virtuals.storages.mem import InMemoryStorage
from virtuals.views import EagerDictView

storage = InMemoryStorage(codec=BinaryCodec())
storage.open()
nav = Navigator(storage)

with storage.transaction() as tx:
    users = nav.root(tx).open_child("users", EagerDictView)
    users["alice"] = {"name": "Alice", "age": 30, "tags": ["admin"]}
    users["bob"] = {"name": "Bob", "age": 25, "tags": []}

    for user_id, profile in users.items():
        print(f"{user_id}: {profile['name']}")

# Under the hood: flat rows with tuple keys, under the "/" root
# ("/", "users", "alice", "name")    -> "Alice"
# ("/", "users", "alice", "age")     -> 30
# ("/", "users", "alice", "tags", 0) -> "admin"

Swap InMemoryStorage for SQLiteStorage, LMDBStorage or RocksDBStorage and nothing else changes. The data is now on disk, shared between processes, and survives a restart.

What you get

  • Native shapes. [], in, len, del, items(), append, set algebra. Nested values are stored as nested structure and read back with their types.
  • Lazy all the way down. Opening a.b.c touches no storage. A read fetches exactly the rows it needs.
  • Many structures, one store. Dict, list, set, frozenset, tuple, bytearray, insertion-ordered dict with paging, fractional-indexed ordered collection with O(1) insert and move, int-keyed map with range sampling. Mix them freely under one root.
  • Real transactions. Serializable, read your own writes, atomic commit, abort on exception. Snapshots bind at open and are never torn.
  • Concurrency that holds up. One writer at a time, queued across threads, coroutines and processes. Coroutines wait without blocking the loop. Contention surfaces as two retryable errors, never as backend noise.
  • Watch anything. Subscribe to a view, a child, its children or a pattern of descendants. Wakes arrive after commit, in-process or across processes over Redis or Valkey, with resync on reconnect and no silent drops.
  • Extensible both ways. Write a new view and it runs on every backend. Write a new backend, run the shipped compliance suites, and every view works on it.

Eager and lazy

Every view that can hold nested containers has two facets over the same rows. Eager reads return Python values, lazy reads return child views you can keep navigating.

from itertools import islice

with storage.snapshot() as snap:
    users = nav.root(snap).open_child("users", EagerDictView)

    assert users["alice"]["tags"] == ["admin"]       # a plain dict

    alice = users.lazy["alice"]                      # a live view, nothing read yet
    assert alice["name"] == "Alice"                  # one row

    admins = [k for k, v in users.lazy.items() if "admin" in v["tags"]]
    first = list(islice(users.lazy.keys(), 1))
    assert admins == ["alice"] and first == ["alice"]

islice, comprehensions and generators compose with lazy views directly, so there is no separate slice or window type to learn.

Ordered collections

FractionalIndexedDictView is a collection keyed by id where order is a first-class operation. Insert, move and delete touch a constant number of rows however large the collection grows, and two writers inserting at the same spot both land.

from virtuals.views import EagerFractionalIndexedDictView

with storage.transaction() as tx:
    board = nav.root(tx).open_child("board", EagerFractionalIndexedDictView)
    board.append("design", {"title": "Design"})
    board.append("ship", {"title": "Ship"})
    board.insert_before("ship", "build", {"title": "Build"})
    board.move_first("ship")

    assert list(board) == ["ship", "design", "build"]
    assert board.next_after("ship") == "design"

LogIndexedDictView keeps insertion order and pages with keys(after=, limit=). Kh57View maps sparse ints and samples uniformly from a key range.

Watching changes

Attach a publisher to the storage and an observer to the same transport. Views build the subscription filter. A watch on a container that does not exist yet still works, and its first write fires it.

from virtuals.observers.mem import InMemoryObserver
from virtuals.publishers.mem import InMemoryPublisher
from virtuals.tkv.transport import InMemoryTransport

transport = InMemoryTransport()
publisher = InMemoryPublisher(transport)
observer = InMemoryObserver(transport)
live = InMemoryStorage(codec=BinaryCodec(), publisher=publisher)
publisher.connect()
observer.connect()
live.open()
live_nav = Navigator(live)

with live.snapshot() as snap:
    watch = live_nav.root(snap).open_child("orders", EagerDictView).on_child_change("o1")

woken = []
observer.subscribe(watch).bind(woken.append)

with live.transaction() as tx:
    live_nav.root(tx).open_child("orders", EagerDictView)["o1"] = {"status": "paid"}
publisher.flush()
observer.flush()
assert woken

publisher.disconnect()
observer.disconnect()
live.close()

Swap the in-memory pair for the Redis publisher and observer and the same watch fires in every process. Receivers re-read state on wake, so repeats and resync wakes are harmless.

Backends

Backend Process model Good for Install
In-memory single tests, caches, ephemeral state built-in
SQLite multi persistent state shared by several processes built-in
LMDB multi persistent, local disk, fastest reads virtuals-py[lmdb]
RocksDB single persistent, write heavy, one owning process virtuals-py[rocksdb]

Every backend passes the same conformance suite: transactions, snapshots, errors, process model, notifications and cost, plus model-based state machines, randomized multi-process load with SIGKILLed writers, and interrupt fuzzing into live commits. A backend ships only if it passes.

Three layers

Each layer adds one concept and knows nothing about the one above it.

View       structure: dict, list, set, ordered, ...
Container  hierarchy: parents, children, kinds
Storage    flat tuple keys in lexicographic order

Storage is a tuple-key KV store. Every read and write goes through a context: a transaction, a snapshot or a write batch. A missing key reads as EMPTY, never None, since None is a value.

from virtuals.tkv.types import EMPTY

with storage.transaction() as tx:
    tx.put(("raw", "alice", "name"), "Alice")

with storage.snapshot() as snap:
    assert snap.get(("raw", "alice", "name")) == "Alice"
    assert snap.get(("raw", "nobody")) is EMPTY

Container reads tuples as paths and keeps the tree honest: a key is a container or a value, never both, and keeps its kind until deleted.

from virtuals import Container

with storage.snapshot() as snap:
    alice = Container(ctx=snap, site=("/", "users", "alice"))
    assert alice.exists()
    assert sorted(alice.iter_child_keys()) == ["age", "name", "tags"]

View turns a container into a data structure, and the registry maps Python types to views so nested values round-trip.

with storage.transaction() as tx:
    users = nav.root(tx).open_child("users", EagerDictView)
    users["carol"] = {"name": "Carol", "age": 41, "tags": {"ops"}}
    assert users["carol"]["tags"] == {"ops"}
    assert len(users) == 3

storage.close()

Building your own

A view is a class over a container. Subclass the bases in virtuals.view and virtuals.collections (they follow the collections.abc pattern: implement a few abstracts, get the rest), register it, and it works on every backend with observability included.

A backend implements the storage protocol. virtuals.testing ships StorageProtocolCompliance, ObserverCompliance and the codec suites as pytest classes: subclass, provide a fixture, run.

Part of Nu

Virtuals is the storage engine under Nu. nustd.kv binds Nu refs to virtuals views, so a Nu program reads and writes durable, observable state with the same calls it uses for in-memory state. Virtuals has no Nu dependency and stands on its own.

Docs

License

Apache-2.0

Metadata

Release files for virtuals-py 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for virtuals-py 0.2.0
File Size Uploaded Provenance Actions
virtuals_py-0.2.0.tar.gz 296.0 kB With provenance Details for virtuals_py-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for virtuals-py 0.2.0
File Interpreter ABI Platform Actions
virtuals_py-0.2.0-py3-none-any.whl Python 3 none any Details for virtuals_py-0.2.0-py3-none-any.whl

Total release size: 671.9 kB

Release files / virtuals_py-0.2.0.tar.gz

Download URL virtuals_py-0.2.0.tar.gz
Size 296.0 kB
Tags Source
SHA-256 checksum
How to use checksums
dd791ddf03b11ebe6c0f865477d816c1804646713762f9bc6135dcb1a3a6bf14
BLAKE2b-256 checksum
How to use checksums
fb106b014e05cdd4dca62d78bca0c9518841033d68c2d494ac72e477cde0ae34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Release files / virtuals_py-0.2.0-py3-none-any.whl

Download URL virtuals_py-0.2.0-py3-none-any.whl
Size 375.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1a8c81c0e71a0dc20a6b22585d9ce0494a03b2018f1e0de9ec8eecacfdcea341
BLAKE2b-256 checksum
How to use checksums
6069da2a66036900bb1fc0f210786adaf94d537bbd2b75393168dae4f2622263
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Security for virtuals-py 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for virtuals_py-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page