daft-lance
Lance integration for Daft.
Install
# Install just the daft-lance extension
pip install daft-lance
# Install daft with the daft-lance extension
pip install 'daft[lance]'
Usage
Compaction
from daft_lance import compact_files
compact_files("s3://bucket/my_dataset")
Scalar Indexing
from daft_lance import create_scalar_index
create_scalar_index("s3://bucket/my_dataset", column="name", index_type="INVERTED")
Column Merging
from daft_lance import merge_columns_df
merge_columns_df(df, "s3://bucket/my_dataset")
Conditional Overwrite
Replace just the rows matching a predicate. One Lance commit deletes them from the existing table and adds the new data, so readers see either the whole replacement or none of it.
import daft_lance
daft_lance.write_lance(
df,
"s3://bucket/events",
mode="insert_overwrite",
overwrite_where="dt = DATE '2026-08-25'",
).collect()
The table must already exist. overwrite_where determines which existing rows are removed;
the input DataFrame is appended as-is. Rows outside overwrite_where are not replaced by a
later re-run, so callers should filter the input first when they need idempotent replacement.
Warning: Lance does not treat a concurrent append or update as conflicting with this commit, so rows another writer adds while the overwrite runs survive it even when they match
overwrite_where, without any error. Make sure no other writer touches the table during a conditional overwrite.
Namespace Tables
Address Lance tables through a Lance Namespace
(catalog) instead of a raw URI. Pass namespace_impl + namespace_properties + table_id
in place of uri — the namespace resolves the table's storage location and vends any storage
credentials. This works across read_lance, write_lance, merge_columns_df,
create_scalar_index, and compact_files.
import daft
import daft_lance
table_id = ["my_table"]
namespace = {"namespace_impl": "dir", "namespace_properties": {"root": "/tmp/lance_tables"}}
daft_lance.write_lance(
daft.from_pydict({"id": [1, 2, 3]}),
table_id=table_id,
mode="create",
**namespace,
).collect()
df = daft_lance.read_lance(table_id=table_id, **namespace)
uri and the namespace parameters are mutually exclusive: provide exactly one of uri or
(namespace_impl + table_id).
Using a REST namespace (e.g. Gravitino Lance REST server)
namespace = {
"namespace_impl": "rest",
"namespace_properties": {"uri": "http://127.0.0.1:9101/lance"},
}
table_id = ["lance_catalog", "sales", "orders"]
daft_lance.write_lance(df, table_id=table_id, mode="create", **namespace).collect()
daft_lance.read_lance(table_id=table_id, **namespace).show()
When the catalog holds the storage configuration (bucket, endpoint, credentials), the
describe_table response vends storage_options to the client, so you do not need to pass
object-store credentials yourself. If the namespace does not vend credentials, your
io_config (or explicit storage_options) is applied to the resolved location; when both
are present, namespace-vended options take precedence.
Namespace clients are cached per (implementation, properties) pair. The cache size defaults
to 16 and can be tuned with the DAFT_LANCE_NAMESPACE_CACHE_SIZE environment variable
(read once at import time).
Daft's own entry points
Native namespace support in daft.read_lance / DataFrame.write_lance is tracked in
Eventual-Inc/Daft#7282; until that lands,
use the daft_lance entry points shown above for namespace-addressed tables.
Migration
The migration only requires replacing daft.io.lance with daft_lance.
# See changes in current directory and all subdirectories
find . -type f -name "*.py" -exec sed 's/daft\.io\.lance/daft_lance/g' {} +
# Apply the changes
find . -type f -name "*.py" -exec sed -i 's/daft\.io\.lance/daft_lance/g' {} +
Blob Support
The daft_lance extension supports Lance BLOB V2 by reading descriptors
into the following daft datatype. Note that daft.read_lance will NOT
materialize Lance BLOB V2 bytes.
{
kind: uint8,
position: uint64,
size: uint64,
blob_id: uint32,
blob_uri: string,
}
To materialize blobs, read the dataset with row IDs enabled and call take_blobs:
import lance
import daft
from daft_lance import take_blobs
ds = lance.dataset("s3://bucket/my_dataset")
df = daft.read_lance(ds.uri, default_scan_options={"with_row_id": True})
df = take_blobs(df, ds, "blob_column")
# each value is a lance.Blob — call .read() to fetch bytes
blobs = df.select("blob_column").to_pydict()["blob_column"]
data = blobs[0].read()
To write binary columns as Lance Blob V2, use the blob_columns opt-in:
import daft
df = daft.from_pydict({"id": [1, 2, 3], "data": [b"...", b"...", b"..."]})
df.write_lance("s3://bucket/my_dataset", blob_columns=["data"]).collect()
Development
Requires uv.
# Sync the development environment
make sync
# Run tests
make test
# Run linting and type checks
make lint
make typecheck
# Format code
make format
# Run all pre-commit hooks
make precommit
# Build sdist and wheel packages
make build
License
Apache-2.0
Metadata
Release files for daft-lance 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| daft_lance-0.5.0.tar.gz | 153.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| daft_lance-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 210.7 kB
Release files / daft_lance-0.5.0.tar.gz
| Download URL | daft_lance-0.5.0.tar.gz |
|---|---|
| Size | 153.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83d49fdccb577e6a93c167ec9baa5e1f69bda7db4eb52a32b0ff05f911c23edd
|
|
BLAKE2b-256 checksum How to use checksums |
5f6b3fafb1d214ead642d3ec0eb78e81541eb2f15211d62367440ebafb5c2e7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 14, 2026.
Transparency logRelease files / daft_lance-0.5.0-py3-none-any.whl
| Download URL | daft_lance-0.5.0-py3-none-any.whl |
|---|---|
| Size | 56.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
22b4c7560751bfea422348d4b11614fb23dc57e66c8c9fc8290980c4f170c369
|
|
BLAKE2b-256 checksum How to use checksums |
0785d89c98792cc0b79b4b1e4130e581da9ae2c849228d18ca31c640b0d66c4b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 14, 2026.
Transparency log