Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@

def _get_cpp_type_from_numpy_type(dtype):
cpptypes = {
"i1": "signed char",
"u1": "unsigned char",
"i2": "Short_t",
"u2": "UShort_t",
"i4": "int",
Expand Down Expand Up @@ -161,7 +163,8 @@ def _AsRVec(arr):
This function returns an RVec which adopts the memory of the given
PyObject. The RVec takes the data pointer and the size from the array
interface dictionary.
Note that for arrays of strings, the input strings are copied into the RVec.
Note that for arrays of strings and int8, the input data is copied into the RVec
rather than adopted.
"""
import math

Expand All @@ -183,7 +186,7 @@ def _AsRVec(arr):
typestr = interface["typestr"]
dtype = typestr[1:]

# Construct an RVec of strings
# Construct an RVec of strings by copying the input array
if dtype == "O" or dtype.startswith("U"):
underlying_object_types = {type(elem) for elem in arr}
if len(underlying_object_types) > 1:
Expand All @@ -198,6 +201,11 @@ def _AsRVec(arr):
else:
raise TypeError("Cannot create an RVec from a numpy array of data type object.")

# Construct an RVec of int8 by copying the input array
# RVec<signed char> does not expose the ptr+size contructor
if dtype == "i1":
return ROOT.VecOps.RVec["signed char"](arr)

if len(typestr) != 3:
raise RuntimeError(
"Object not convertible: __array_interface__['typestr'] returned '"
Expand Down
31 changes: 22 additions & 9 deletions bindings/pyroot/pythonizations/test/rvec_asrvec.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,20 @@ class AsRVec(unittest.TestCase):
"""

# Helpers
dtypes = ["int16", "int32", "int64", "uint16", "uint32", "uint64", "float32", "float64", "bool"]
dtypes = ["int8", "uint8", "int16", "int32", "int64", "uint16", "uint32", "uint64", "float32", "float64", "bool"]

def check_memory_adoption(self, root_obj, np_obj):
np_obj[0] = get_maximum_for_dtype(np_obj.dtype)
np_obj[1] = get_minimum_for_dtype(np_obj.dtype)
self.assertEqual(root_obj[0], np_obj[0])
self.assertEqual(root_obj[1], np_obj[1])
def check_memory_adoption(self, root_obj, np_obj, copy=False):
if not copy:
np_obj[0] = get_maximum_for_dtype(np_obj.dtype)
np_obj[1] = get_minimum_for_dtype(np_obj.dtype)
for i in range(2):
root_val = root_obj[i]
# RVec<unsigned char> elements are returned as Python strings (e.g. '\x7f' instead of 127),
# we need to convert them back to integers for comparison
if isinstance(root_val, str):
root_val = ord(root_val)

self.assertEqual(root_val, np_obj[i])

def check_size(self, expected_size, obj):
self.assertEqual(expected_size, obj.size())
Expand All @@ -44,9 +51,15 @@ def test_dtypes(self):
Test adoption of numpy arrays with different data types
"""
for dtype in self.dtypes:
np_obj = np.empty(2, dtype=dtype)
root_obj = ROOT.VecOps.AsRVec(np_obj)
self.check_memory_adoption(root_obj, np_obj)
if dtype == "int8":
# For int8 we use explicit small values to avoid platform-dependent signed char representation issues
np_obj = np.array([42, 35], dtype="int8")
root_obj = ROOT.VecOps.AsRVec(np_obj)
self.check_memory_adoption(root_obj, np_obj, copy=True)
else:
np_obj = np.empty(2, dtype=dtype)
root_obj = ROOT.VecOps.AsRVec(np_obj)
self.check_memory_adoption(root_obj, np_obj)
self.check_size(2, root_obj)

def test_object_dtype_strings(self):
Expand Down