Files
lancedb/python/python/tests/test_experimental_functions.py
T
Xuanwo 8a4effa06a fix: gate experimental local functions behind an opt-in feature (#4688)
Published SDK builds currently expose local SQL computed columns and
materialized-view execution that are not ready for release. Add the
opt-in `experimental-functions` Cargo feature to the Rust core and
forward it through the Python and Node bindings; published packages
leave it disabled.

Local declarations and refreshes now return `NotSupported` before
mutating tables or spawning jobs, including prepared materialized-view
creation. Remote Cloud/Enterprise client operations, Python UDF
packaging, ordinary eager `add_columns`, and access to existing data
remain available. Stored metadata and write protections stay active, and
shared server commit helpers remain usable.

CI covers both feature configurations. Existing local execution tests
run with the feature enabled; default-build regressions cover early
rejection, unchanged table versions, existing metadata protections, and
normal eager transforms. API and source-build documentation explain the
opt-in.

Validation: Rust library tests pass with the feature disabled (1,869
tests) and enabled (1,947 tests), plus three default-build integration
regressions. Python and TypeScript bindings were rebuilt and tested in
both configurations, including mocked remote requests; Rust formatting,
Python package lint, TypeScript lint/build, and generated docs were
checked. Repository-wide Ruff still reports 48 pre-existing errors in
unchanged `docs/web-tests` files.
2026-10-27 14:32:09 +08:00

59 lines
2.3 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
import lancedb
import pytest
pytestmark = pytest.mark.skipif(
lancedb._lancedb._experimental_functions_enabled,
reason="tests the default build without experimental-functions",
)
@pytest.mark.parametrize("namespace", [False, True])
def test_default_build_rejects_local_functions(tmp_path, namespace):
db = (
lancedb.connect_namespace("dir", {"root": str(tmp_path)})
if namespace
else lancedb.connect(tmp_path)
)
table = db.create_table("source", [{"x": 1}, {"x": 2}])
version = table.version
operations = [
lambda: table.add_columns(computed={"doubled": "x * 2"}),
lambda: table.refresh_column("x"),
lambda: table.refresh_column_async("x"),
lambda: db.create_materialized_view("blocked", "source"),
lambda: db.create_materialized_view("blocked", "source", with_no_data=True),
lambda: db.create_materialized_view_async("blocked", "source"),
]
for operation in operations:
with pytest.raises(NotImplementedError, match="experimental-functions"):
operation()
assert table.version == version
assert db.table_names() == ["source"]
table.add_columns({"eager": "x * 2"})
assert table.to_arrow()["eager"].to_pylist() == [2, 4]
@pytest.mark.asyncio
async def test_async_default_build_rejects_local_functions(tmp_path):
db = await lancedb.connect_async(tmp_path)
table = await db.create_table("source", [{"x": 1}, {"x": 2}])
version = await table.version()
operations = [
lambda: table.add_columns(computed={"doubled": "x * 2"}),
lambda: table.refresh_column("x"),
lambda: table.refresh_column_async("x"),
lambda: db.create_materialized_view("blocked", "source"),
lambda: db.create_materialized_view("blocked", "source", with_no_data=True),
lambda: db.create_materialized_view_async("blocked", "source"),
]
for operation in operations:
with pytest.raises(NotImplementedError, match="experimental-functions"):
await operation()
assert await table.version() == version
assert await db.table_names() == ["source"]
await table.add_columns({"eager": "x * 2"})
assert (await table.query().to_arrow())["eager"].to_pylist() == [2, 4]