Skip to content

Commit 5b59ed7

Browse files
Code review changes
- Rewrites the note on waveform of strings - Refactors _require_waveform - Removes unused DTYPE and add more tests for mixed lists
1 parent 84346b8 commit 5b59ed7

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

docs/reference/api.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,9 @@ All functions return a wrapped `ProcessDeviceSupportIn` or
343343
if given, or defaults to ``'FLOAT'``.
344344

345345
.. note::
346-
When storing arrays of strings, it is possible to store Unicode characters.
347-
However, as EPICS has no Unicode support the resultant values will be stored
348-
as byte strings. Care must be taken when encoding/decoding the values.
346+
Storing arrays of strings differs from other values. String arrays will always
347+
be assumed to be encoded as Unicode strings, and will be returned to the user
348+
as a Python list rather than a Numpy array.
349349

350350

351351
The following functions generates specialised records.

softioc/device.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,18 +367,18 @@ def to_epics_str_array(value):
367367

368368

369369
def _require_waveform(value, dtype):
370-
if isinstance(value, bytes):
371-
# Special case hack for byte arrays. Surprisingly tricky:
372-
value = numpy.frombuffer(value, dtype = numpy.uint8)
373-
374370
if dtype and dtype.char == 'S':
375371
return to_epics_str_array(value)
376-
377-
value = numpy.require(value, dtype = dtype)
378-
if value.shape == ():
379-
value.shape = (1,)
380-
assert value.ndim == 1, 'Can\'t write multidimensional arrays'
381-
return value
372+
else:
373+
if isinstance(value, bytes):
374+
# Special case hack for byte arrays. Surprisingly tricky:
375+
value = numpy.frombuffer(value, dtype = numpy.uint8)
376+
377+
value = numpy.require(value, dtype = dtype)
378+
if value.shape == ():
379+
value.shape = (1,)
380+
assert value.ndim == 1, 'Can\'t write multidimensional arrays'
381+
return value
382382

383383

384384
class WaveformBase(ProcessDeviceSupportCore):

tests/test_record_values.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@
3131
"might think to put into a record that can theoretically hold a huge " \
3232
"string and so lets test it and prove that shall we?"
3333

34-
# The numpy dtype of all arrays of strings
35-
NUMPY_DTYPE_STRING = "S40"
36-
3734

3835
def record_func_names(fixture_value):
3936
"""Provide a nice name for the record_func fixture"""
@@ -234,6 +231,34 @@ def record_values_names(fixture_value):
234231
["123abc", "456def", "7890ghi"],
235232
list,
236233
),
234+
(
235+
"wIn_mixed_array_1",
236+
builder.WaveformIn,
237+
["123abc", 456, "7890ghi"],
238+
["123abc", "456", "7890ghi"],
239+
list,
240+
),
241+
(
242+
"wOut_mixed_array_1",
243+
builder.WaveformOut,
244+
["123abc", 456, "7890ghi"],
245+
["123abc", "456", "7890ghi"],
246+
list,
247+
),
248+
(
249+
"wIn_mixed_array_2",
250+
builder.WaveformIn,
251+
[123, 456, "7890ghi"],
252+
["123", "456", "7890ghi"],
253+
list,
254+
),
255+
(
256+
"wOut_mixed_array_2",
257+
builder.WaveformOut,
258+
[123, 456, "7890ghi"],
259+
["123", "456", "7890ghi"],
260+
list,
261+
),
237262
(
238263
"longStringIn_str",
239264
builder.longStringIn,
@@ -435,7 +460,7 @@ def is_valid(configuration):
435460

436461
if creation_func in (builder.WaveformIn, builder.WaveformOut):
437462
if isinstance(initial_value, list) and \
438-
all(isinstance(val, (str, bytes)) for val in initial_value):
463+
any(isinstance(val, (str, bytes)) for val in initial_value):
439464
if set_enum is not SetValueEnum.INITIAL_VALUE:
440465
print(f"Removing {configuration}")
441466
return False
@@ -580,7 +605,7 @@ def test_value_pre_init_set(self, record_values):
580605
if (
581606
creation_func in [builder.WaveformIn, builder.WaveformOut] and
582607
isinstance(initial_value, list) and
583-
all(isinstance(s, (str, bytes)) for s in initial_value)
608+
any(isinstance(s, (str, bytes)) for s in initial_value)
584609
):
585610
pytest.skip("Cannot .set() a list of strings to a waveform, must"
586611
"initially specify using initial_value or FTVL")
@@ -974,7 +999,7 @@ def test_waveform_rejects_overlong_values(self):
974999

9751000
def test_waveform_rejects_late_strings(self):
9761001
"""Test that a waveform won't allow a list of strings to be assigned
977-
if no list was given in initial waveform construction"""
1002+
if no string list was given in initial waveform construction"""
9781003
w_in = builder.WaveformIn("W_IN", length=10)
9791004
w_out = builder.WaveformOut("W_OUT", length=10)
9801005

@@ -994,11 +1019,13 @@ def test_waveform_rejects_long_array_of_strings(self):
9941019
initial_value=["123abc", "456def", "7890ghi"]
9951020
)
9961021

1022+
# Test putting too many elements
9971023
with pytest.raises(AssertionError):
9981024
w_in.set(["1", "2", "3", "4"])
9991025
with pytest.raises(AssertionError):
10001026
w_out.set(["1", "2", "3", "4"])
10011027

1028+
# Test putting too long a string
10021029
with pytest.raises(ValueError):
10031030
w_in.set([VERY_LONG_STRING])
10041031
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)