From 29d788bb94d7f954e6a328f3ba4a84e04ca8aea0 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Thu, 24 Jul 2025 16:24:30 +0200 Subject: [PATCH] Vector types: optimize error path The pure-python implementations of `Vector.from_native` perform type checks. This patch makes the driver fail fast when passing incompatible types instead of scanning the whole Iterable parameter. --- src/neo4j/vector.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/neo4j/vector.py b/src/neo4j/vector.py index ca01ad76..6a20d5cf 100644 --- a/src/neo4j/vector.py +++ b/src/neo4j/vector.py @@ -753,10 +753,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_float = tuple(item for item in data if not isinstance(item, float)) + non_float_gen = (item for item in data if not isinstance(item, float)) + non_float = next(non_float_gen, None) if non_float: raise TypeError( - f"Cannot build f64 vector from {type(non_float[0]).__name__}, " + f"Cannot build f64 vector from {type(non_float).__name__}, " "expected float." ) return cls(_np.fromiter(data, dtype=_np.dtype(">f8")).tobytes()) @@ -826,10 +827,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_float = tuple(item for item in data if not isinstance(item, float)) + non_float_gen = (item for item in data if not isinstance(item, float)) + non_float = next(non_float_gen, None) if non_float: raise TypeError( - f"Cannot build f32 vector from {type(non_float[0]).__name__}, " + f"Cannot build f32 vector from {type(non_float).__name__}, " "expected float." ) return cls(_np.fromiter(data, dtype=_np.dtype(">f4")).tobytes()) @@ -903,10 +905,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_int = tuple(item for item in data if not isinstance(item, int)) + non_int_gen = (item for item in data if not isinstance(item, int)) + non_int = next(non_int_gen, None) if non_int: raise TypeError( - f"Cannot build i64 vector from {type(non_int[0]).__name__}, " + f"Cannot build i64 vector from {type(non_int).__name__}, " "expected int." ) data = _t.cast(tuple[int, ...], data) @@ -994,10 +997,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_int = tuple(item for item in data if not isinstance(item, int)) + non_int_gen = (item for item in data if not isinstance(item, int)) + non_int = next(non_int_gen, None) if non_int: raise TypeError( - f"Cannot build i32 vector from {type(non_int[0]).__name__}, " + f"Cannot build i32 vector from {type(non_int).__name__}, " "expected int." ) data = _t.cast(tuple[int, ...], data) @@ -1085,10 +1089,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_int = tuple(item for item in data if not isinstance(item, int)) + non_int_gen = (item for item in data if not isinstance(item, int)) + non_int = next(non_int_gen, None) if non_int: raise TypeError( - f"Cannot build i16 vector from {type(non_int[0]).__name__}, " + f"Cannot build i16 vector from {type(non_int).__name__}, " "expected int." ) data = _t.cast(tuple[int, ...], data) @@ -1176,10 +1181,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self: @classmethod def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self: data = tuple(data) - non_int = tuple(item for item in data if not isinstance(item, int)) + non_int_gen = (item for item in data if not isinstance(item, int)) + non_int = next(non_int_gen, None) if non_int: raise TypeError( - f"Cannot build i8 vector from {type(non_int[0]).__name__}, " + f"Cannot build i8 vector from {type(non_int).__name__}, " "expected int." ) data = _t.cast(tuple[int, ...], data)