Skip to main content

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)

Source distribution for daft-lance 0.5.0
File Size Uploaded
daft_lance-0.5.0.tar.gz 153.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for daft-lance 0.5.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

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