handle Sequence subclasses (list/tuple/...); convert object arrays to strings (like the default for non-array types); convert built-in bools to ints (like numpy boolean arrays)

This commit is contained in:
2021-03-05 21:21:36 +00:00
parent 344768713d
commit 76d8a73828
2 changed files with 25 additions and 9 deletions

View File

@ -1,3 +1,4 @@
from collections.abc import Sequence
import numpy as np import numpy as np
@ -33,11 +34,15 @@ class PVInfo:
def infer_type(value, parse_string=True): #TODO lists/tuples/...? char vs. strings? def infer_type(value, parse_string=True): #TODO char vs. strings?
if isinstance(value, Sequence) and not isinstance(value, str):
value = np.array(value) # rely on numpy to figure out a consistent dtype
if isinstance(value, np.ndarray): if isinstance(value, np.ndarray):
return infer_type_numpy(value) #TODO: also parse strings for np arrays?! return infer_type_numpy(value) #TODO: also parse strings for np arrays?!
elif isinstance(value, np.generic): # covers all numpy scalars elif isinstance(value, np.generic): # covers all numpy scalars
value = value.item() value = value.item()
return infer_type_scalar(value, parse_string=parse_string) return infer_type_scalar(value, parse_string=parse_string)
@ -74,8 +79,9 @@ def infer_type_scalar(value, parse_string=True):
def infer_type_numpy(arr): def infer_type_numpy(arr):
# cannot infer a single type from object arrays, default to string representation
if arr.dtype == object: if arr.dtype == object:
raise NotImplementedError("cannot infer a single type from object arrays") return "string", str(arr.tolist())
# bool -> int # bool -> int
if arr.dtype == bool: if arr.dtype == bool:
@ -83,7 +89,7 @@ def infer_type_numpy(arr):
# epics only allows 1D, and needs conversion to built-in data types # epics only allows 1D, and needs conversion to built-in data types
lst = arr.ravel().tolist() lst = arr.ravel().tolist()
dtype, _ = infer_type_scalar(lst[0]) dtype, _ = infer_type_scalar(lst[0]) # TODO: is taking the first element safe? object arrays are ruled out above...
count = len(lst) count = len(lst)
return dtype, lst, count return dtype, lst, count

View File

@ -66,11 +66,9 @@ def test_it_None():
assert it(None) == ("string", "None") assert it(None) == ("string", "None")
def test_it_True(): def test_it_True():
# assert it(True) == ("string", "True")
assert it(True) == ("int", 1) assert it(True) == ("int", 1)
def test_it_False(): def test_it_False():
# assert it(False) == ("string", "False")
assert it(False) == ("int", 0) assert it(False) == ("int", 0)
def test_it_nan(): def test_it_nan():
@ -116,11 +114,9 @@ def test_it_np1D_bool():
def test_it_np1D_object(): def test_it_np1D_object():
arr = np.array([None]) # gives dtype=object arr = np.array([None]) # gives dtype=object
with pytest.raises(NotImplementedError): assert it(arr) == ("string", "[None]")
it(arr)
arr = np.array(["test", 123, 1.23], dtype=object) arr = np.array(["test", 123, 1.23], dtype=object)
with pytest.raises(NotImplementedError): assert it(arr) == ("string", "['test', 123, 1.23]")
it(arr)
def test_it_np_scalar_int(): def test_it_np_scalar_int():
@ -137,6 +133,20 @@ def test_it_np_scalar_bool():
scalar = np.array(True)[()] scalar = np.array(True)[()]
ref = scalar.item() ref = scalar.item()
assert it(scalar) == ("int", 1) assert it(scalar) == ("int", 1)
scalar = np.array(False)[()]
ref = scalar.item()
assert it(scalar) == ("int", 0)
def test_it_list():
dat = [0, 1, 2]
ref = dat
assert it(dat) == ("int", ref, len(ref))
def test_it_tuple():
dat = (0, 1, 2)
ref = list(dat)
assert it(dat) == ("int", ref, len(ref))