Skip to content

Commit ee9caf3

Browse files
authored
Vector types: optimize error path (#1226)
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.
1 parent a4d3f30 commit ee9caf3

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

src/neo4j/vector.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
]
5454

5555

56+
_DEFAULT = object()
57+
58+
5659
class Vector:
5760
r"""
5861
A class representing a Neo4j vector.
@@ -753,10 +756,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
753756
@classmethod
754757
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
755758
data = tuple(data)
756-
non_float = tuple(item for item in data if not isinstance(item, float))
757-
if non_float:
759+
non_float_gen = (item for item in data if not isinstance(item, float))
760+
non_float = next(non_float_gen, _DEFAULT)
761+
if non_float is not _DEFAULT:
758762
raise TypeError(
759-
f"Cannot build f64 vector from {type(non_float[0]).__name__}, "
763+
f"Cannot build f64 vector from {type(non_float).__name__}, "
760764
"expected float."
761765
)
762766
return cls(_np.fromiter(data, dtype=_np.dtype(">f8")).tobytes())
@@ -826,10 +830,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
826830
@classmethod
827831
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
828832
data = tuple(data)
829-
non_float = tuple(item for item in data if not isinstance(item, float))
830-
if non_float:
833+
non_float_gen = (item for item in data if not isinstance(item, float))
834+
non_float = next(non_float_gen, _DEFAULT)
835+
if non_float is not _DEFAULT:
831836
raise TypeError(
832-
f"Cannot build f32 vector from {type(non_float[0]).__name__}, "
837+
f"Cannot build f32 vector from {type(non_float).__name__}, "
833838
"expected float."
834839
)
835840
return cls(_np.fromiter(data, dtype=_np.dtype(">f4")).tobytes())
@@ -903,10 +908,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
903908
@classmethod
904909
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
905910
data = tuple(data)
906-
non_int = tuple(item for item in data if not isinstance(item, int))
907-
if non_int:
911+
non_int_gen = (item for item in data if not isinstance(item, int))
912+
non_int = next(non_int_gen, _DEFAULT)
913+
if non_int is not _DEFAULT:
908914
raise TypeError(
909-
f"Cannot build i64 vector from {type(non_int[0]).__name__}, "
915+
f"Cannot build i64 vector from {type(non_int).__name__}, "
910916
"expected int."
911917
)
912918
data = _t.cast(tuple[int, ...], data)
@@ -994,10 +1000,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
9941000
@classmethod
9951001
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
9961002
data = tuple(data)
997-
non_int = tuple(item for item in data if not isinstance(item, int))
998-
if non_int:
1003+
non_int_gen = (item for item in data if not isinstance(item, int))
1004+
non_int = next(non_int_gen, _DEFAULT)
1005+
if non_int is not _DEFAULT:
9991006
raise TypeError(
1000-
f"Cannot build i32 vector from {type(non_int[0]).__name__}, "
1007+
f"Cannot build i32 vector from {type(non_int).__name__}, "
10011008
"expected int."
10021009
)
10031010
data = _t.cast(tuple[int, ...], data)
@@ -1085,10 +1092,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
10851092
@classmethod
10861093
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
10871094
data = tuple(data)
1088-
non_int = tuple(item for item in data if not isinstance(item, int))
1089-
if non_int:
1095+
non_int_gen = (item for item in data if not isinstance(item, int))
1096+
non_int = next(non_int_gen, _DEFAULT)
1097+
if non_int is not _DEFAULT:
10901098
raise TypeError(
1091-
f"Cannot build i16 vector from {type(non_int[0]).__name__}, "
1099+
f"Cannot build i16 vector from {type(non_int).__name__}, "
10921100
"expected int."
10931101
)
10941102
data = _t.cast(tuple[int, ...], data)
@@ -1176,10 +1184,11 @@ def _from_native_rust(cls, data: _t.Iterable[object], /) -> _t.Self:
11761184
@classmethod
11771185
def _from_native_np(cls, data: _t.Iterable[object], /) -> _t.Self:
11781186
data = tuple(data)
1179-
non_int = tuple(item for item in data if not isinstance(item, int))
1180-
if non_int:
1187+
non_int_gen = (item for item in data if not isinstance(item, int))
1188+
non_int = next(non_int_gen, _DEFAULT)
1189+
if non_int is not _DEFAULT:
11811190
raise TypeError(
1182-
f"Cannot build i8 vector from {type(non_int[0]).__name__}, "
1191+
f"Cannot build i8 vector from {type(non_int).__name__}, "
11831192
"expected int."
11841193
)
11851194
data = _t.cast(tuple[int, ...], data)

0 commit comments

Comments
 (0)