From fc44535ceeac0fd73ae94d0a0151c35753cb3e40 Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:31:30 +0800 Subject: [PATCH] fix(python): clarify bare Vector annotations (#3809) ## Summary - raise a clear `TypeError` when `Vector` is used without a dimension - preserve normal `Vector(dim)` behavior across Pydantic v1 and v2 - add a regression test that defines a model without importing PyArrow ## Root cause Pydantic interpreted the bare `Vector` factory as a callable field type and inspected its postponed annotations in the user model's namespace. Because that namespace did not define LanceDB's internal `pa` alias, model construction failed with the misleading `NameError: name 'pa' is not defined` instead of explaining that `Vector` must be parameterized. The factory now exposes Pydantic's v1 and v2 schema hooks and rejects bare use before signature introspection with guidance to use `Vector(dim)`. ## Validation - `uvx --from 'ruff==0.15.20' ruff check .` - `uvx --from 'ruff==0.15.20' ruff format --check python/python/lancedb/pydantic.py python/python/tests/test_pydantic.py` - `cd python && uv run --extra tests pytest python/tests/test_pydantic.py::test_bare_vector_raises_clear_error -q` - `cd python && uv run --extra tests pytest python/tests/test_pydantic.py -q` - compatibility checks with Pydantic 1.10.22, 2.11.4, and 2.13.4 Fixes #2384 Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> --- python/python/lancedb/pydantic.py | 10 ++++++++++ python/python/tests/test_pydantic.py | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/python/python/lancedb/pydantic.py b/python/python/lancedb/pydantic.py index c4dedc0e6..1ab6e6fcc 100644 --- a/python/python/lancedb/pydantic.py +++ b/python/python/lancedb/pydantic.py @@ -153,6 +153,16 @@ def Vector( return FixedSizeList +def _raise_bare_vector_error(*_args): + raise TypeError("Vector must be parameterized with a dimension, e.g. Vector(128).") + + +# Pydantic v1 and v2 otherwise treat the bare Vector factory as a field validator +# and inspect its signature, which produces misleading errors about internal types. +setattr(Vector, "__get_validators__", _raise_bare_vector_error) +setattr(Vector, "__get_pydantic_core_schema__", _raise_bare_vector_error) + + def MultiVector( dim: int, value_type: pa.DataType = pa.float32(), nullable: bool = True ) -> Type: diff --git a/python/python/tests/test_pydantic.py b/python/python/tests/test_pydantic.py index e1d533784..db93d7c64 100644 --- a/python/python/tests/test_pydantic.py +++ b/python/python/tests/test_pydantic.py @@ -415,6 +415,17 @@ def test_nullable_vector(): assert schema == pa.schema([pa.field("vec", pa.list_(pa.float32(), 16), True)]) +def test_bare_vector_raises_clear_error(): + namespace = { + "__name__": "test_model_without_pyarrow", + "LanceModel": LanceModel, + "Vector": Vector, + } + + with pytest.raises(TypeError, match=r"Vector must be parameterized.*Vector\(128\)"): + exec("class TestModel(LanceModel):\n vector: Vector", namespace) + + def test_fixed_size_list_field(): class TestModel(pydantic.BaseModel): vec: Vector(16)