forked from M-Labs/artiq
language: Support appending to datasets
This commit is contained in:
parent
820326960e
commit
bf84226c7d
|
@ -20,6 +20,8 @@ ARTIQ-5
|
||||||
edge timestamps are not required. See :mod:`artiq.coredevice.edge_counter` for
|
edge timestamps are not required. See :mod:`artiq.coredevice.edge_counter` for
|
||||||
the core device driver and :mod:`artiq.gateware.rtio.phy.edge_counter`/
|
the core device driver and :mod:`artiq.gateware.rtio.phy.edge_counter`/
|
||||||
:meth:`artiq.gateware.eem.DIO.add_std` for the gateware components.
|
:meth:`artiq.gateware.eem.DIO.add_std` for the gateware components.
|
||||||
|
* List datasets can now be efficiently appended to from experiments using
|
||||||
|
:meth:`artiq.language.environment.HasEnvironment.append_to_dataset`.
|
||||||
* The controller manager now ignores device database entries without the
|
* The controller manager now ignores device database entries without the
|
||||||
``"command"`` key set to facilitate sharing of devices between multiple
|
``"command"`` key set to facilitate sharing of devices between multiple
|
||||||
masters.
|
masters.
|
||||||
|
|
|
@ -321,6 +321,18 @@ class HasEnvironment:
|
||||||
as ``slice(*sub_tuple)`` (multi-dimensional slicing)."""
|
as ``slice(*sub_tuple)`` (multi-dimensional slicing)."""
|
||||||
self.__dataset_mgr.mutate(key, index, value)
|
self.__dataset_mgr.mutate(key, index, value)
|
||||||
|
|
||||||
|
@rpc(flags={"async"})
|
||||||
|
def append_to_dataset(self, key, value):
|
||||||
|
"""Append a value to a dataset.
|
||||||
|
|
||||||
|
The target dataset must be a list (i.e. support ``append()``), and must
|
||||||
|
have previously been set from this experiment.
|
||||||
|
|
||||||
|
The broadcast/persist/archive mode of the given key remains unchanged
|
||||||
|
from when the dataset was last set. Appended values are transmitted
|
||||||
|
efficiently as incremental modifications in broadcast mode."""
|
||||||
|
self.__dataset_mgr.append_to(key, value)
|
||||||
|
|
||||||
def get_dataset(self, key, default=NoDefault, archive=True):
|
def get_dataset(self, key, default=NoDefault, archive=True):
|
||||||
"""Returns the contents of a dataset.
|
"""Returns the contents of a dataset.
|
||||||
|
|
||||||
|
|
|
@ -136,17 +136,18 @@ class DatasetManager:
|
||||||
elif key in self.local:
|
elif key in self.local:
|
||||||
del self.local[key]
|
del self.local[key]
|
||||||
|
|
||||||
def mutate(self, key, index, value):
|
def _get_mutation_target(self, key):
|
||||||
target = None
|
target = self.local.get(key, None)
|
||||||
if key in self.local:
|
|
||||||
target = self.local[key]
|
|
||||||
if key in self._broadcaster.raw_view:
|
if key in self._broadcaster.raw_view:
|
||||||
if target is not None:
|
if target is not None:
|
||||||
assert target is self._broadcaster.raw_view[key][1]
|
assert target is self._broadcaster.raw_view[key][1]
|
||||||
target = self._broadcaster[key][1]
|
return self._broadcaster[key][1]
|
||||||
if target is None:
|
if target is None:
|
||||||
raise KeyError("Cannot mutate non-existing dataset")
|
raise KeyError("Cannot mutate nonexistent dataset '{}'".format(key))
|
||||||
|
return target
|
||||||
|
|
||||||
|
def mutate(self, key, index, value):
|
||||||
|
target = self._get_mutation_target(key)
|
||||||
if isinstance(index, tuple):
|
if isinstance(index, tuple):
|
||||||
if isinstance(index[0], tuple):
|
if isinstance(index[0], tuple):
|
||||||
index = tuple(slice(*e) for e in index)
|
index = tuple(slice(*e) for e in index)
|
||||||
|
@ -154,6 +155,9 @@ class DatasetManager:
|
||||||
index = slice(*index)
|
index = slice(*index)
|
||||||
setitem(target, index, value)
|
setitem(target, index, value)
|
||||||
|
|
||||||
|
def append_to(self, key, value):
|
||||||
|
self._get_mutation_target(key).append(value)
|
||||||
|
|
||||||
def get(self, key, archive=False):
|
def get(self, key, archive=False):
|
||||||
if key in self.local:
|
if key in self.local:
|
||||||
return self.local[key]
|
return self.local[key]
|
||||||
|
|
|
@ -32,6 +32,9 @@ class TestExperiment(EnvExperiment):
|
||||||
def set(self, key, value, **kwargs):
|
def set(self, key, value, **kwargs):
|
||||||
self.set_dataset(key, value, **kwargs)
|
self.set_dataset(key, value, **kwargs)
|
||||||
|
|
||||||
|
def append(self, key, value):
|
||||||
|
self.append_to_dataset(key, value)
|
||||||
|
|
||||||
|
|
||||||
KEY = "foo"
|
KEY = "foo"
|
||||||
|
|
||||||
|
@ -67,3 +70,35 @@ class ExperimentDatasetCase(unittest.TestCase):
|
||||||
self.assertEqual(self.exp.get(KEY), 1)
|
self.assertEqual(self.exp.get(KEY), 1)
|
||||||
with self.assertRaises(KeyError):
|
with self.assertRaises(KeyError):
|
||||||
self.dataset_db.get(KEY)
|
self.dataset_db.get(KEY)
|
||||||
|
|
||||||
|
def test_append_local(self):
|
||||||
|
self.exp.set(KEY, [])
|
||||||
|
self.exp.append(KEY, 0)
|
||||||
|
self.assertEqual(self.exp.get(KEY), [0])
|
||||||
|
self.exp.append(KEY, 1)
|
||||||
|
self.assertEqual(self.exp.get(KEY), [0, 1])
|
||||||
|
|
||||||
|
def test_append_broadcast(self):
|
||||||
|
self.exp.set(KEY, [], broadcast=True)
|
||||||
|
self.exp.append(KEY, 0)
|
||||||
|
self.assertEqual(self.dataset_db.data[KEY][1], [0])
|
||||||
|
self.exp.append(KEY, 1)
|
||||||
|
self.assertEqual(self.dataset_db.data[KEY][1], [0, 1])
|
||||||
|
|
||||||
|
def test_append_array(self):
|
||||||
|
for broadcast in (True, False):
|
||||||
|
self.exp.set(KEY, [], broadcast=broadcast)
|
||||||
|
self.exp.append(KEY, [])
|
||||||
|
self.exp.append(KEY, [])
|
||||||
|
self.assertEqual(self.exp.get(KEY), [[], []])
|
||||||
|
|
||||||
|
def test_append_scalar_fails(self):
|
||||||
|
for broadcast in (True, False):
|
||||||
|
with self.assertRaises(AttributeError):
|
||||||
|
self.exp.set(KEY, 0, broadcast=broadcast)
|
||||||
|
self.exp.append(KEY, 1)
|
||||||
|
|
||||||
|
def test_append_nonexistent_fails(self):
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
self.exp.append(KEY, 0)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue