Skip to content

Commit c912871

Browse files
committed
ENH: add infer_datakeys helper
This will extract the shape, (json)dtype, and the detailed numpy dtype information for a value.
1 parent 686faa8 commit c912871

2 files changed

Lines changed: 93 additions & 1 deletion

File tree

event_model/_numpy.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import json
2-
2+
import typing
33
import numpy
44

5+
from ._errors import EventModelValueError
6+
57

68
def sanitize_doc(doc):
79
"""Return a copy with any numpy objects converted to built-in Python types.
@@ -51,3 +53,49 @@ def default(self, obj):
5153
return obj.item()
5254
return obj.tolist()
5355
return json.JSONEncoder.default(self, obj)
56+
57+
58+
def infer_datakeys(val):
59+
"""
60+
Given a value, infer what the datatype (as Ewent Model would describe it).
61+
62+
Parameters
63+
----------
64+
val : Any
65+
66+
"""
67+
bad_iterables = (str, bytes, dict)
68+
_type_map = {
69+
"number": (float, numpy.floating, complex),
70+
"array": (numpy.ndarray, list, tuple),
71+
"string": (str,),
72+
"integer": (int, numpy.integer),
73+
}
74+
75+
if isinstance(val, typing.Iterable) and not isinstance(val, bad_iterables):
76+
dtype = "array"
77+
else:
78+
for json_type, py_types in _type_map.items():
79+
if isinstance(val, py_types):
80+
dtype = json_type
81+
break
82+
else:
83+
raise EventModelValueError(
84+
f"Cannot determine the appropriate bluesky-friendly data type for "
85+
f"value {val} of Python type {type(val)}. "
86+
f"Supported types include: int, float, str, and iterables such as "
87+
f"list, tuple, np.ndarray, and so on."
88+
)
89+
90+
# this should only make a copy if it _has to_. If we have lots of
91+
# non-already-numpy arrays flowing through and this is doing things like
92+
# computing huge dask arrays etc.
93+
arr_val = numpy.asanyarray(val)
94+
arr_dtype = arr_val.dtype
95+
96+
return {
97+
"dtype": dtype,
98+
"dtype_str": arr_dtype.str,
99+
"dtype_descr": arr_dtype.descr,
100+
"shape": list(arr_val.shape),
101+
}

event_model/tests/test_numpy.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pytest
2+
import numpy as np
3+
4+
5+
from event_model._numpy import infer_datakeys
6+
from event_model._errors import EventModelValueError
7+
8+
9+
@pytest.mark.parametrize("shape", [[1], [2, 2]])
10+
@pytest.mark.parametrize(
11+
"dtype", ["i8", "f2", "c16", np.dtype([("a", "i"), ("b", "f")])]
12+
)
13+
def test_infer_dtypes_array(shape, dtype):
14+
v = np.ones(shape, dtype=dtype)
15+
if isinstance(dtype, str):
16+
dtype = np.dtype(dtype)
17+
18+
ret = infer_datakeys(v)
19+
20+
assert ret["dtype"] == "array"
21+
assert ret["shape"] == list(shape)
22+
assert ret["dtype_str"] == dtype.str
23+
assert ret["dtype_descr"] == dtype.descr
24+
25+
26+
@pytest.mark.parametrize("val", [{}, b"bob"])
27+
def test_infer_fail(val):
28+
with pytest.raises(EventModelValueError):
29+
infer_datakeys(val)
30+
31+
32+
@pytest.mark.parametrize(
33+
"value,dtype",
34+
[("bob", "string"), (1, "integer"), (1.0, "number"), (1 + 1j, "number")],
35+
)
36+
def test_infer_dtypes_scalar(value, dtype):
37+
38+
ret = infer_datakeys(value)
39+
np_dt = np.array(value).dtype
40+
assert ret["dtype"] == dtype
41+
assert ret["shape"] == []
42+
43+
assert ret["dtype_str"] == np_dt.str
44+
assert ret["dtype_descr"] == np_dt.descr

0 commit comments

Comments
 (0)