SQLite-backed embedded database for records, objects, and vectors in one local file.
Project description
Zova Python Bindings
This package contains the source-first Python bindings for Zova.
It is a PyO3/maturin extension backed by the safe Rust zova binding. It does
not wrap the C ABI directly with ctypes or cffi. The native build still uses
Zova's C ABI underneath through the Rust zova-sys crate, so Python gets the
same records/objects/vectors foundation without reimplementing the ABI ownership
rules.
Contents
- How It Fits
- Install
- Local Development
- What It Covers
- Savepoints
- Operational Safety
- Objects
- Vectors
- SQL-Native Vector Search
How It Fits
Python users import zova. The extension is built with PyO3 and reuses the
safe Rust binding, which in turn links Zova's C ABI.
flowchart LR
App["Python app"]
PyPkg["zova Python package"]
PyO3["PyO3 extension"]
Rust["Rust zova crate"]
CABI["libzova_c.a"]
File["local .zova file"]
App --> PyPkg
PyPkg --> PyO3
PyO3 --> Rust
Rust --> CABI
CABI --> File
Install
After the Python package is published to PyPI:
python -m pip install zova
The v0.17 Python package is source-first. It builds the PyO3 extension locally
through maturin and Cargo, and the Rust crates zova and zova-sys must be
available on crates.io first. Users need Python 3.10 or newer, Rust/Cargo, Zig
0.16.0 or newer, and a working C compiler/linker.
No official platform wheel matrix is promised in v0.17.
Local Development
From bindings/python:
uv run --isolated --with maturin --with pytest maturin develop
uv run --isolated --with pytest python -m pytest
The native build uses maturin, Cargo, Zig, and the Rust zova crate. Users do
not need to locate a shared C library manually.
The Python API is pre-1.0 and may still change alongside the Rust binding.
What It Covers
The Python package exposes database lifecycle, conversion, prepared SQL statements, transactions, explicit vacuum, backup/compact/restore, objects, streaming object writes, vectors, SQL-native vector search, context managers, and Zova status exceptions.
One Python Database object owns one native handle. The native C ABI serializes
calls on that handle, so one handle is safe but not parallel. Open additional
database handles when an application needs independent concurrent connections;
SQLite locking rules still apply across handles. PyO3 classes remain unsendable
in this release even though the native C ABI serializes its own calls.
Use Database.open(path, read_only=True) for read-only handles, and
Database.set_busy_timeout(milliseconds) when an application wants SQLite to
wait briefly on cross-handle contention. No nonzero timeout is installed by
default.
Use Database.last_insert_rowid(), Database.changes(),
Database.total_changes(), and Statement.column_name(index) for normal
application SQL record helpers. They do not expose or stabilize Zova's private
_zova_* tables.
Savepoints
Use explicit savepoints for partial rollback inside one database connection:
with zova.Database.open("app.zova") as db:
db.begin_immediate()
db.savepoint("attach_file")
db.exec("insert into attachments(filename) values ('draft.txt')")
db.rollback_to_savepoint("attach_file")
db.release_savepoint("attach_file")
db.commit()
Savepoint names are strict ASCII identifiers: 1-64 bytes, first byte
[A-Za-z_], remaining bytes [A-Za-z0-9_], and no case-insensitive _zova_
prefix. rollback_to_savepoint() keeps the savepoint active;
release_savepoint() removes it.
An inner released savepoint can still be undone by rolling back an outer
transaction or savepoint.
Use savepoint_context() when you want rollback cleanup tied to a with block:
with db.savepoint_context("attach_file") as scoped_db:
scoped_db.exec("insert into attachments(filename) values ('draft.txt')")
On normal exit the context releases the savepoint. On exception it rolls back, releases, and then re-raises the original exception when cleanup succeeds.
Operational Safety
Use backup_to() for a faithful snapshot, compact_to() for a
space-reclaiming copy, and restore_backup() to copy a backup into a new
destination file. Destinations must be .zova paths and are never overwritten.
with zova.Database.open("app.zova") as db:
db.backup_to("app.backup.zova")
db.compact_to("app.compact.zova")
zova.restore_backup("app.backup.zova", "app.restored.zova")
By default, each operation verifies the destination after copying. Pass
verify=False only when you will verify separately, for example with
zova check --deep.
Diagnostic recovery commands such as zova doctor, zova salvage --dry-run,
and zova salvage <source> <destination> are CLI-first in the v0.16 line. The
Python package does not expose typed doctor/salvage report APIs yet, and library
code should not parse the human text output as a stable binding contract.
Objects
The Python binding exposes Zova objects as content-addressed byte values while keeping application metadata in normal SQL tables.
import zova
with zova.Database.create("app.zova") as db:
db.exec(
"create table attachments("
"id integer primary key, "
"filename text not null, "
"object_id blob not null)"
)
object_id = db.put_object(b"hello from Zova")
with db.prepare("insert into attachments(filename, object_id) values (?1, ?2)") as stmt:
stmt.bind_text(1, "hello.txt")
stmt.bind_blob(2, bytes(object_id))
stmt.step()
assert db.read_object_range(object_id, 0, 5) == b"hello"
For large inputs, use ObjectWriter so the full object does not have to be held
in memory by the caller:
with db.object_writer() as writer:
writer.write(b"chunk one")
writer.write(b"chunk two")
object_id = writer.finish()
If a writer leaves the context without finish(), it is cancelled and any
unreferenced chunks written by that writer are cleaned up. Writer operations
follow Zova's object transaction policy and reject active user transactions.
Loose chunks and assembly are also exposed for receive-side workflows:
applications track transfer state in their own SQL tables, call
put_object_chunk() for verified chunks, then call
assemble_object_from_chunks() when the manifest is complete.
Vectors
The Python binding exposes Zova vectors with the same model as the Rust binding: Zova stores numeric vectors in named collections, while application metadata stays in normal SQL tables.
import zova
with zova.Database.create("vectors.zova") as db:
db.exec(
"create table chunks("
"id text primary key, "
"document_id text not null, "
"text text not null, "
"vector_id text not null)"
)
db.create_vector_collection(
"chunks",
zova.VectorCollectionOptions(2, zova.VectorMetric.L2),
)
db.put_vectors(
"chunks",
[
zova.VectorInput("chunk:1", [0.0, 0.0]),
zova.VectorInput("chunk:2", [1.0, 0.0]),
],
)
db.exec(
"insert into chunks(id, document_id, text, vector_id) values "
"('c1', 'doc-a', 'first chunk', 'chunk:1'), "
"('c2', 'doc-a', 'near chunk', 'chunk:2')"
)
results = db.search_vectors_in(
"chunks",
[0.0, 0.0],
["chunk:1", "chunk:2"],
2,
)
for result in results:
with db.prepare("select text from chunks where vector_id = ?1") as stmt:
stmt.bind_text(1, result.id)
stmt.step()
print(result.id, result.distance, stmt.column_text(0))
Search is exact and lower distance is better. Candidate-filtered searches skip
missing ids and deduplicate duplicate candidates. Search-by-id excludes the
source vector. Threshold variants are inclusive, and dot-product thresholds may
be negative because dot distance is -dot_product.
Deleting a vector collection removes Zova's private vector rows only. User SQL metadata rows that reference vector ids are application-owned and remain in place.
SQL-Native Vector Search
Zova registers SQL vector functions and the zova_vector_search virtual table
on Zova database connections. Bind query vectors as little-endian f32 blobs
with encode_f32_le():
query = zova.encode_f32_le([0.0, 0.0])
with db.prepare(
"select c.id, zova_vector_distance('chunks', c.vector_id, ?1) as distance "
"from chunks as c "
"where c.document_id = 'doc-a' "
"order by distance "
"limit 10"
) as stmt:
stmt.bind_blob(1, query)
Row-to-row distances use zova_vector_distance_by_id(collection, vector_id, source_vector_id). Collection-wide SQL search uses zova_vector_search:
with db.prepare(
"select c.text, s.distance "
"from zova_vector_search as s "
"join chunks as c on c.vector_id = s.vector_id "
"where s.collection = 'chunks' "
"and s.query_vector = ?1 "
"and s.top_k = 10 "
"order by s.rank"
) as stmt:
stmt.bind_blob(1, query)
Python's built-in sqlite3 module opens an ordinary SQLite connection and does
not automatically register Zova's SQL functions or virtual table. Use
zova.Database for SQL-native vector search in this binding. A future SQLite
loadable extension may make the SQL surface available to external SQLite
connections.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zova-0.17.0.tar.gz.
File metadata
- Download URL: zova-0.17.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72cdaa36ce2d7929f6555997a48b3ce906b6e7dbf0e5b32236e72337e18c80a1
|
|
| MD5 |
a43d29d7a2be445e59d312a8a509697a
|
|
| BLAKE2b-256 |
fef531ad52bff046b0e0dc6ff9758b94e90b223e45503fcec2f14dede4e72bf1
|
File details
Details for the file zova-0.17.0-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: zova-0.17.0-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ec60acfc37b338a9d0b9bbb6f4d9b240c9a91ad1a5119842d316688e9dcc2ec
|
|
| MD5 |
bb3bbfc21e60da943ef8eca0857ddc71
|
|
| BLAKE2b-256 |
27018f288e2698c2d1e2f517ba48a059fcd1432a3984d6eb9136e87759e0cbb1
|