Skip to content

Commit d18edde

Browse files
fix(numpy) - Pass correct contiguous array to C level
1 parent d7ba027 commit d18edde

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

rayforce/types/containers/vector.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@
4545
"int64": r.TYPE_I64,
4646
"float64": r.TYPE_F64,
4747
}
48+
_RAY_TO_NUMPY: dict[int, str] = {
49+
r.TYPE_U8: "uint8",
50+
r.TYPE_B8: "bool",
51+
r.TYPE_I16: "int16",
52+
r.TYPE_I32: "int32",
53+
r.TYPE_I64: "int64",
54+
r.TYPE_F64: "float64",
55+
}
4856
# Auto-widen numpy dtypes that have no direct rayforce equivalent
4957
_NUMPY_WIDEN: dict[str, str] = {
5058
"float16": "float64",
@@ -222,6 +230,9 @@ def from_numpy(cls, arr: t.Any, *, ray_type: type[RayObject] | int | None = None
222230

223231
if ray_type is not None and arr.dtype.kind not in ("M", "m"):
224232
type_code = abs(ray_type if isinstance(ray_type, int) else ray_type.type_code)
233+
target_dtype = _RAY_TO_NUMPY.get(type_code)
234+
if target_dtype is not None and arr.dtype.name != target_dtype:
235+
arr = np.ascontiguousarray(arr.astype(target_dtype))
225236
return cls(ptr=FFI.init_vector_from_raw_buffer(type_code, len(arr), arr.data))
226237

227238
if (maybe_code := _NUMPY_TO_RAY.get(arr.dtype.name)) is not None:

tests/test_numpy_compatibility.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,17 +1867,29 @@ def test_1d_still_works(self):
18671867
# ============================================================================
18681868

18691869

1870-
class TestFromNumpyExplicitRayTypeMismatch:
1871-
"""Explicit ray_type with buffer element size mismatch."""
1870+
class TestFromNumpyExplicitRayTypeCast:
1871+
"""Explicit ray_type with different numpy dtype casts values correctly."""
18721872

1873-
def test_float32_as_f64_raises(self):
1874-
"""float32 (4 bytes) → F64 (8 bytes): buffer too small."""
1873+
def test_float32_as_f64(self):
1874+
"""float32 → F64: values are cast, not reinterpreted."""
18751875
arr = np.array([1.0, 2.0], dtype=np.float32)
1876-
with pytest.raises(ValueError, match="Buffer too small"):
1877-
t.Vector.from_numpy(arr, ray_type=t.F64)
1876+
vec = t.Vector.from_numpy(arr, ray_type=t.F64)
1877+
assert vec.to_list() == [1.0, 2.0]
18781878

1879-
def test_int16_as_i64_raises(self):
1880-
"""int16 (2 bytes) → I64 (8 bytes): buffer too small."""
1879+
def test_int16_as_i64(self):
1880+
"""int16 → I64: values are cast, not reinterpreted."""
18811881
arr = np.array([1, 2], dtype=np.int16)
1882-
with pytest.raises(ValueError, match="Buffer too small"):
1883-
t.Vector.from_numpy(arr, ray_type=t.I64)
1882+
vec = t.Vector.from_numpy(arr, ray_type=t.I64)
1883+
assert vec.to_list() == [1, 2]
1884+
1885+
def test_int64_as_i16(self):
1886+
"""int64 → I16: values are cast, not reinterpreted."""
1887+
arr = np.array([1, 2, 3, 4], dtype=np.int64)
1888+
vec = t.Vector.from_numpy(arr, ray_type=t.I16)
1889+
assert vec.to_list() == [1, 2, 3, 4]
1890+
1891+
def test_int64_as_i32(self):
1892+
"""int64 → I32: values are cast, not reinterpreted."""
1893+
arr = np.array([1, 2, 3, 4], dtype=np.int64)
1894+
vec = t.Vector.from_numpy(arr, ray_type=t.I32)
1895+
assert vec.to_list() == [1, 2, 3, 4]

0 commit comments

Comments
 (0)