Exemplo n.º 1
0
def test_verify_filled(filler):
    "Test the utility function verify_filled."
    with pytest.raises(event_model.UnfilledData):
        event_model.verify_filled(event_model.pack_event_page(raw_event))
    event = copy.deepcopy(raw_event)
    name, doc = filler('event', event)
    event_model.verify_filled(event_model.pack_event_page(event))
Exemplo n.º 2
0
    def event_page(self, doc):
        '''Add event page document information to a ".tiff" file.

        This method adds event_page document information to a ".tiff" file,
        creating it if nesecary.

        .. warning::

            All non 2D 'image like' data is explicitly ignored.

        .. note::

            The data in Events might be structured as an Event, an EventPage,
            or a "bulk event" (deprecated). The DocumentRouter base class takes
            care of first transforming the other representations into an
            EventPage and then routing them through here, so no further action
            is required in this class. We can assume we will always receive an
            EventPage.

        Parameters:
        -----------
        doc : dict
            EventPage document
        '''
        event_model.verify_filled(doc)
        descriptor = self._descriptors[doc['descriptor']]
        stream_name = descriptor.get('name')
        for field in doc['data']:
            for img in doc['data'][field]:
                # Check that the data is 2D or 3D; if not ignore it.
                data_key = descriptor['data_keys'][field]
                ndim = len(data_key['shape'] or [])
                if data_key['dtype'] == 'array' and 1 < ndim < 4:
                    # there is data to be written so
                    # create a file for this stream and field
                    # if one does not exist yet
                    if not self._tiff_writers.get(stream_name, {}).get(field):
                        filename = get_prefixed_filename(
                            file_prefix=self._file_prefix,
                            start_doc=self._start,
                            stream_name=stream_name,
                            field=field)
                        file = self._manager.open('stream_data', filename,
                                                  'xb')
                        tw = TiffWriter(file, **self._init_kwargs)
                        self._tiff_writers[stream_name][field] = tw

                    # write the data
                    img_asarray = numpy.asarray(img, dtype=self._astype)
                    if ndim == 2:
                        # handle 2D data just like 3D data
                        # by adding a 3rd dimension
                        img_asarray = numpy.expand_dims(img_asarray, axis=0)
                    for i in range(img_asarray.shape[0]):
                        img_asarray_2d = img_asarray[i, :]
                        # append the image to the file
                        tw = self._tiff_writers[stream_name][field]
                        tw.save(img_asarray_2d, *self._kwargs)
Exemplo n.º 3
0
    def event(self, doc):
        '''Add event document information to a ".tiff" file.

        This method adds event document information to a ".tiff" file,
        creating it if necessary.

        .. warning::

            All non 2D 'image-like' data is explicitly ignored.

        .. note::

            The data in Events might be structured as an Event, an EventPage,
            or a "bulk event" (deprecated). The DocumentRouter base class takes
            care of first transforming the other representations into an
            EventPage and then routing them through here, as we require Event
            documents _in this case_ we overwrite both the `event` method and
            the `event_page` method so we can assume we will always receive an
            Event.

        Parameters:
        -----------
        doc : dict
            Event document
        '''
        event_model.verify_filled(event_model.pack_event_page(*[doc]))
        descriptor = self._descriptors[doc['descriptor']]
        stream_name = descriptor.get('name')
        for field in doc['data']:
            img = doc['data'][field]
            # Check that the data is 2D or 3D; if not ignore it.
            data_key = descriptor['data_keys'][field]
            ndim = len(data_key['shape'] or [])
            if data_key['dtype'] == 'array' and 1 < ndim < 4:
                img_asarray = numpy.asarray(img, dtype=self._astype)
                if ndim == 2:
                    # handle 2D data just like 3D data
                    # by adding a 3rd dimension
                    img_asarray = numpy.expand_dims(img_asarray, axis=0)
                for i in range(img_asarray.shape[0]):
                    img_asarray_2d = img_asarray[i, :]
                    num = next(self._counter[stream_name][field])
                    filename = get_prefixed_filename(
                        file_prefix=self._file_prefix,
                        start_doc=self._start,
                        descriptor_doc=descriptor,
                        event_doc=doc,
                        num=num,
                        stream_name=stream_name,
                        field=field)
                    file = self._manager.open('stream_data', filename, 'xb')
                    tw = TiffWriter(file, **self._init_kwargs)
                    self._tiff_writers[stream_name][field + f'-{num}'] = tw
                    tw.save(img_asarray_2d, *self._kwargs)
Exemplo n.º 4
0
    def event_page(self, doc):
        doc = super().event_page(doc)
        verify_filled(doc)
        stream_name, = doc['data']
        dev_name = self.descriptor_doc['data_keys'][stream_name]['devname']

        raw_data, = doc['data'][stream_name]

        def load_adc_trace(df_raw):
            df = pd.DataFrame()
            df['timestamp'] = df_raw['ts_s'] + 1e-9 * df_raw['ts_ns']
            df['adc'] = df_raw['adc'].apply(
                lambda x: (int(x, 16) >> 8) - 0x40000 if (int(x, 16) >> 8) >
                0x1FFFF else int(x, 16) >> 8) * 7.62939453125e-05
            return df

        def load_enc_trace(df_raw):
            df = pd.DataFrame()
            df['timestamp'] = df_raw['ts_s'] + 1e-9 * df_raw['ts_ns']
            df['encoder'] = df_raw['encoder'].apply(
                lambda x: int(x) if int(x) <= 0 else -(int(x) ^ 0xffffff - 1))
            return df

        def load_trig_trace(df):
            df['timestamp'] = df['ts_s'] + 1e-9 * df['ts_ns']
            df = df.iloc[::2]
            return df.iloc[:, [5, 3]]

        data = pd.DataFrame()
        stream_source = self.descriptor_doc['data_keys'][stream_name]['source']

        if stream_source == 'pizzabox-di-file':
            data = load_trig_trace(raw_data)

        if stream_source == 'pizzabox-adc-file':
            data = load_adc_trace(raw_data)
            stream_offset = f'{stream_name} offset'
            if stream_offset in self.start_doc:
                data.iloc[:, 1] -= self.start_doc[stream_offset]
            stream_gain = f'{stream_name} gain'
            if stream_gain in self.start_doc:
                data.iloc[:, 1] /= 10**self.start_doc[stream_gain]

        if stream_source == 'pizzabox-enc-file':
            data = load_enc_trace(raw_data)
            if dev_name == 'hhm_theta':
                data.iloc[:, 1] = xas.xray.encoder2energy(
                    data['encoder'], 360000,
                    -float(self.start_doc['angle_offset']))
                dev_name = 'energy'

        self._preprocessed_data[dev_name] = data
Exemplo n.º 5
0
    def event_page(self, doc):
        '''Add event page document information to a ".csv" file.

        This method adds event_page document information to a ".csv" file,
        creating it if nesecary.

        .. warning::

            All non 1D 'tabular' data is explicitly ignored.

        .. note::

            The data in Events might be structured as an Event, an EventPage,
            or a "bulk event" (deprecated). The DocumentRouter base class takes
            care of first transforming the other repsentations into an
            EventPage and then routing them through here, so no further action
            is required in this class. We can assume we will always receive an
            EventPage.

        Parameters:
        -----------
        doc : dict
            EventPage document
        '''
        event_model.verify_filled(doc)
        streamname = self._streamnames[doc['descriptor']]
        valid_data = {}
        for field in doc['data']:
            # check that the data is 1D, if not ignore it
            if numpy.asarray(doc['data'][field]).ndim == 1:
                # create a file for this stream and field if required
                if streamname not in self._files.keys():
                    filename = (f'{self._templated_file_prefix}'
                                f'{streamname}.csv')
                    f = self._manager.open('stream_data', filename, 'xt')
                    self._files[streamname] = f
                # add the valid data to the valid_data dict
                valid_data[field] = doc['data'][field]

        if valid_data:
            event_data = pandas.DataFrame(
                valid_data, index=doc[self._kwargs['index_label']])
            event_data['seq_num'] = doc['seq_num']

            if self._initial_header_kwarg:
                self._kwargs['header'] = streamname not in self._has_header

            file = self._files[streamname]
            event_data.to_csv(file, **self._kwargs)
            if self._flush:
                file.flush()
            self._has_header.add(streamname)
Exemplo n.º 6
0
    def event_page(self, doc):
        '''Add event page document information to a ".tiff" file.

        This method adds event_page document information to a ".tiff" file,
        creating it if nesecary.

        .. warning::

            All non 2D 'image like' data is explicitly ignored.

        .. note::

            The data in Events might be structured as an Event, an EventPage,
            or a "bulk event" (deprecated). The DocumentRouter base class takes
            care of first transforming the other repsentations into an
            EventPage and then routing them through here, so no further action
            is required in this class. We can assume we will always receive an
            EventPage.

        Parameters:
        -----------
        doc : dict
            EventPage document
        '''
        event_model.verify_filled(doc)
        streamname = self._descriptors[doc['descriptor']].get('name')
        self._templated_file_prefix = self._file_prefix.format(
            start=self._start)
        for field in doc['data']:
            for img in doc['data'][field]:
                # check that the data is 2D, if not ignore it
                img_asarray = numpy.asarray(img,
                                            dtype=numpy.dtype(self._astype))
                if img_asarray.ndim == 2:
                    # create a file for this stream and field if required
                    if not self._tiff_writers.get(streamname, {}).get(field):
                        filename = (f'{self._templated_file_prefix}'
                                    f'{streamname}-{field}.tiff')
                        file = self._manager.open('stream_data', filename,
                                                  'xb')
                        tw = TiffWriter(file, **self._init_kwargs)
                        self._tiff_writers[streamname][field] = tw
                    # append the image to the file
                    tw = self._tiff_writers[streamname][field]
                    tw.save(img_asarray, *self._kwargs)
Exemplo n.º 7
0
    def event(self, doc):
        '''Add event document information to a ".tiff" file.

        This method adds event document information to a ".tiff" file,
        creating it if necessary.

        .. warning::

            All non 2D 'image-like' data is explicitly ignored.

        .. note::

            The data in Events might be structured as an Event, an EventPage,
            or a "bulk event" (deprecated). The DocumentRouter base class takes
            care of first transforming the other repsentations into an
            EventPage and then routing them through here, as we require Event
            documents _in this case_ we overwrite both the `event` method and
            the `event_page` method so we can assume we will always receive an
            Event.

        Parameters:
        -----------
        doc : dict
            Event document
        '''
        event_model.verify_filled(event_model.pack_event_page(*[doc]))
        descriptor = self._descriptors[doc['descriptor']]
        streamname = descriptor.get('name')
        for field in doc['data']:
            img = doc['data'][field]
            # check that the data is 2D, if not ignore it
            img_asarray = numpy.asarray(img, dtype=self._astype)
            if img_asarray.ndim == 2:
                # template the file name.
                self._templated_file_prefix = self._file_prefix.format(
                    start=self._start, descriptor=descriptor, event=doc)
                num = next(self._counter[streamname][field])
                filename = (f'{self._templated_file_prefix}'
                            f'{streamname}-{field}-{num}.tiff')
                file = self._manager.open('stream_data', filename, 'xb')
                tw = TiffWriter(file, **self._init_kwargs)
                self._tiff_writers[streamname][field + f'-{num}'] = tw
                tw.save(img_asarray, *self._kwargs)
Exemplo n.º 8
0
def test_filler(tmp_path):
    class DummyHandler:
        def __init__(self, resource_path, a, b):
            assert a == 1
            assert b == 2
            assert resource_path == str(tmp_path / "stack.tiff")

        def __call__(self, c, d):
            assert c == 3
            assert d == 4
            return numpy.ones((5, 5))

    path_root = str(tmp_path)

    reg = {'DUMMY': DummyHandler}
    filler = event_model.Filler(reg, inplace=True)
    run_bundle = event_model.compose_run()
    desc_bundle = run_bundle.compose_descriptor(data_keys={
        'motor': {
            'shape': [],
            'dtype': 'number',
            'source': '...'
        },
        'image': {
            'shape': [512, 512],
            'dtype': 'number',
            'source': '...',
            'external': 'FILESTORE:'
        }
    },
                                                name='primary')
    desc_bundle_baseline = run_bundle.compose_descriptor(
        data_keys={'motor': {
            'shape': [],
            'dtype': 'number',
            'source': '...'
        }},
        name='baseline')
    res_bundle = run_bundle.compose_resource(spec='DUMMY',
                                             root=path_root,
                                             resource_path='stack.tiff',
                                             resource_kwargs={
                                                 'a': 1,
                                                 'b': 2
                                             })
    datum_doc = res_bundle.compose_datum(datum_kwargs={'c': 3, 'd': 4})
    raw_event = desc_bundle.compose_event(data={
        'motor': 0,
        'image': datum_doc['datum_id']
    },
                                          timestamps={
                                              'motor': 0,
                                              'image': 0
                                          },
                                          filled={'image': False},
                                          seq_num=1)
    filler('start', run_bundle.start_doc)
    filler('descriptor', desc_bundle.descriptor_doc)
    filler('descriptor', desc_bundle_baseline.descriptor_doc)
    filler('resource', res_bundle.resource_doc)
    filler('datum', datum_doc)
    event = copy.deepcopy(raw_event)
    assert isinstance(event['data']['image'], str)
    filler('event', event)
    stop_doc = run_bundle.compose_stop()
    filler('stop', stop_doc)
    assert event['data']['image'].shape == (5, 5)
    assert not filler._closed

    # Test NoFiller.
    filler = event_model.NoFiller(reg)
    filler('start', run_bundle.start_doc)
    filler('descriptor', desc_bundle.descriptor_doc)
    filler('descriptor', desc_bundle_baseline.descriptor_doc)
    filler('resource', res_bundle.resource_doc)
    filler('datum', datum_doc)
    event = copy.deepcopy(raw_event)
    assert isinstance(event['data']['image'], str)
    filler('event', event)
    # Check that it *hasn't* been filled.
    assert isinstance(event['data']['image'], str)
    filler('stop', stop_doc)

    # Test get_handler() method.
    handler = filler.get_handler(res_bundle.resource_doc)
    # The method does not expose the internal cache of handlers, so it should
    # not return the same instance when called repeatedly.
    assert filler.get_handler(res_bundle.resource_doc) is not handler

    # Test closing.
    filler.close()
    with pytest.raises(event_model.EventModelRuntimeError):
        filler.get_handler(res_bundle.resource_doc)
    with pytest.raises(event_model.EventModelRuntimeError):
        filler('stop', stop_doc)

    # Test context manager with Event.
    with event_model.Filler(reg, inplace=True) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        name, doc = filler('event', event)
        assert name == 'event'
        assert doc is event
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)
    assert filler._closed

    # Test context manager with EventPage.
    with event_model.Filler(reg, inplace=True) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        name, doc = filler('event_page', event_page)
        assert name == 'event_page'
        assert doc is event_page
        filler('stop', stop_doc)
        assert not filler._closed
    assert event_page['data']['image'][0].shape == (5, 5)
    assert filler._closed

    # Test undefined handler spec
    with event_model.Filler({}, inplace=True) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        assert isinstance(event['data']['image'], str)
        with pytest.raises(event_model.UndefinedAssetSpecification):
            filler('event', event)

    # Test exclude and include.
    with pytest.raises(ValueError):
        event_model.Filler({}, include=[], exclude=[], inplace=True)

    with pytest.warns(DeprecationWarning):
        with event_model.Filler(reg, exclude=['image'],
                                inplace=True) as filler:
            filler('start', run_bundle.start_doc)
            filler('descriptor', desc_bundle.descriptor_doc)
            filler('descriptor', desc_bundle_baseline.descriptor_doc)
            filler('resource', res_bundle.resource_doc)
            filler('datum', datum_doc)
            event = copy.deepcopy(raw_event)
            assert isinstance(event['data']['image'], str)
            filler('event', event)
            filler('stop', stop_doc)

    with pytest.warns(DeprecationWarning):
        with event_model.Filler(reg, include=['image'],
                                inplace=True) as filler:
            filler('start', run_bundle.start_doc)
            filler('descriptor', desc_bundle.descriptor_doc)
            filler('descriptor', desc_bundle_baseline.descriptor_doc)
            filler('resource', res_bundle.resource_doc)
            filler('datum', datum_doc)
            event = copy.deepcopy(raw_event)
            filler('event', event)
            filler('stop', stop_doc)
            assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    with pytest.warns(DeprecationWarning):
        with event_model.Filler(reg,
                                include=['image', 'EXTRA THING'],
                                inplace=True) as filler:
            filler('start', run_bundle.start_doc)
            filler('descriptor', desc_bundle.descriptor_doc)
            filler('descriptor', desc_bundle_baseline.descriptor_doc)
            filler('resource', res_bundle.resource_doc)
            filler('datum', datum_doc)
            event = copy.deepcopy(raw_event)
            filler('event', event)
            filler('stop', stop_doc)
            assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    class DummyHandlerRootMapTest:
        def __init__(self, resource_path, a, b):
            assert a == 1
            assert b == 2
            assert resource_path == str(tmp_path / "moved" / "stack.tiff")

        def __call__(self, c, d):
            assert c == 3
            assert d == 4
            return numpy.ones((5, 5))

    with event_model.Filler({'DUMMY': DummyHandlerRootMapTest},
                            root_map={path_root: str(tmp_path / "moved")},
                            inplace=True) as filler:

        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        filler('event', event)
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    # Test verify_filled.
    with pytest.raises(event_model.UnfilledData):
        event_model.verify_filled(event_model.pack_event_page(raw_event))
    event_model.verify_filled(event_model.pack_event_page(event))

    # Test inplace.
    with event_model.Filler(reg, inplace=True) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        # Test event()
        event = copy.deepcopy(raw_event)
        name, filled_event = filler('event', event)
        assert filled_event is event
        event = copy.deepcopy(raw_event)
        # Test fill_event()
        filled_event = filler.fill_event(event)
        assert filled_event is event
        # Test event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        _, filled_event_page = filler('event_page', event_page)
        assert filled_event_page is event_page
        # Test fill_event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        filled_event_page = filler.fill_event_page(event_page)
        assert filled_event_page is event_page

        # Test fill_event and fill_event_page again with inplace=False.

        # Test fill_event()
        filled_event = filler.fill_event(event, inplace=False)
        assert filled_event is not event
        # Test fill_event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        filled_event_page = filler.fill_event_page(event_page, inplace=False)
        assert filled_event_page is not event_page

    with event_model.Filler(reg, inplace=False) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        name, filled_event = filler('event', event)
        assert filled_event is not event
        assert isinstance(event['data']['image'], str)

        event = copy.deepcopy(raw_event)
        # Test fill_event()
        filled_event = filler.fill_event(event)
        assert filled_event is not event
        # Test event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        _, filled_event_page = filler('event_page', event_page)
        assert filled_event_page is not event_page
        # Test fill_event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        filled_event_page = filler.fill_event_page(event_page)
        assert filled_event_page is not event_page

        # Test fill_event and fill_event_page again with inplace=True.

        # Test fill_event()
        filled_event = filler.fill_event(event, inplace=True)
        assert filled_event is event
        # Test fill_event_page()
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        filled_event_page = filler.fill_event_page(event_page, inplace=True)
        assert filled_event_page is event_page

    with pytest.warns(UserWarning):
        filler = event_model.Filler(reg)

    class OtherDummyHandler:
        "Same as DummyHandler, but a different object to test mutating reg"

        def __init__(self, resource_path, a, b):
            assert a == 1
            assert b == 2
            assert resource_path == str(tmp_path / "stack.tiff")

        def __call__(self, c, d):
            assert c == 3
            assert d == 4
            return numpy.ones((5, 5))

    with event_model.Filler(reg, inplace=False) as filler:
        with pytest.raises(event_model.EventModelTypeError):
            # Updating an existing key fails.
            filler.handler_registry['DUMMY'] = OtherDummyHandler
        with pytest.raises(event_model.EventModelTypeError):
            # Setting a new key fails.
            filler.handler_registry['SOMETHING_ELSE'] = OtherDummyHandler
        with pytest.raises(event_model.EventModelTypeError):
            # Deleting a item fails.
            del filler.handler_registry['DUMMY']
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        name, filled_event = filler('event', event)
        assert filled_event is not event
        assert isinstance(event['data']['image'], str)
        # Now there should be a handler instance in the cache.
        assert filler._handler_cache  # implementation detail
        with pytest.raises(event_model.DuplicateHandler):
            filler.register_handler('DUMMY', OtherDummyHandler)
        filler.register_handler('DUMMY', OtherDummyHandler, overwrite=True)
        assert filler.handler_registry['DUMMY'] is OtherDummyHandler
        # Replacing the handler for a given spec should clear the cache.
        assert not filler._handler_cache  # implementation detail
        # Filling should work the same....
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        name, filled_event = filler('event', event)
        assert filled_event is not event
        assert isinstance(event['data']['image'], str)
        filler.deregister_handler('DUMMY')
        assert not filler.handler_registry
        assert not filler._handler_cache  # implementation detail
Exemplo n.º 9
0
def test_filler(tmp_path):
    class DummyHandler:
        def __init__(self, resource_path, a, b):
            assert a == 1
            assert b == 2
            assert resource_path == str(tmp_path / "stack.tiff")

        def __call__(self, c, d):
            assert c == 3
            assert d == 4
            return numpy.ones((5, 5))

    path_root = str(tmp_path)

    reg = {'DUMMY': DummyHandler}
    filler = event_model.Filler(reg)
    run_bundle = event_model.compose_run()
    desc_bundle = run_bundle.compose_descriptor(data_keys={
        'motor': {
            'shape': [],
            'dtype': 'number',
            'source': '...'
        },
        'image': {
            'shape': [512, 512],
            'dtype': 'number',
            'source': '...',
            'external': 'FILESTORE:'
        }
    },
                                                name='primary')
    desc_bundle_baseline = run_bundle.compose_descriptor(
        data_keys={'motor': {
            'shape': [],
            'dtype': 'number',
            'source': '...'
        }},
        name='baseline')
    res_bundle = run_bundle.compose_resource(spec='DUMMY',
                                             root=path_root,
                                             resource_path='stack.tiff',
                                             resource_kwargs={
                                                 'a': 1,
                                                 'b': 2
                                             })
    datum_doc = res_bundle.compose_datum(datum_kwargs={'c': 3, 'd': 4})
    raw_event = desc_bundle.compose_event(data={
        'motor': 0,
        'image': datum_doc['datum_id']
    },
                                          timestamps={
                                              'motor': 0,
                                              'image': 0
                                          },
                                          filled={'image': False},
                                          seq_num=1)
    filler('start', run_bundle.start_doc)
    filler('descriptor', desc_bundle.descriptor_doc)
    filler('descriptor', desc_bundle_baseline.descriptor_doc)
    filler('resource', res_bundle.resource_doc)
    filler('datum', datum_doc)
    event = copy.deepcopy(raw_event)
    assert isinstance(event['data']['image'], str)
    filler('event', event)
    stop_doc = run_bundle.compose_stop()
    filler('stop', stop_doc)
    assert event['data']['image'].shape == (5, 5)
    assert not filler._closed
    filler.close()
    assert filler._closed

    # Test context manager with Event.
    with event_model.Filler(reg) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        name, doc = filler('event', event)
        assert name == 'event'
        assert doc is event
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)
    assert filler._closed

    # Test context manager with EventPage.
    with event_model.Filler(reg) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event_page = event_model.pack_event_page(copy.deepcopy(raw_event))
        name, doc = filler('event_page', event_page)
        assert name == 'event_page'
        assert doc is event_page
        filler('stop', stop_doc)
        assert not filler._closed
    assert event_page['data']['image'][0].shape == (5, 5)
    assert filler._closed

    # Test undefined handler spec
    with event_model.Filler({}) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        assert isinstance(event['data']['image'], str)
        with pytest.raises(event_model.UndefinedAssetSpecification):
            filler('event', event)

    # Test exclude and include.
    with pytest.raises(ValueError):
        event_model.Filler({}, include=[], exclude=[])

    with event_model.Filler(reg, exclude=['image']) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        assert isinstance(event['data']['image'], str)
        filler('event', event)
        filler('stop', stop_doc)

    with event_model.Filler(reg, include=['image']) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        filler('event', event)
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    with event_model.Filler(reg, include=['image', 'EXTRA THING']) as filler:
        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        filler('event', event)
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    class DummyHandlerRootMapTest:
        def __init__(self, resource_path, a, b):
            assert a == 1
            assert b == 2
            assert resource_path == str(tmp_path / "moved" / "stack.tiff")

        def __call__(self, c, d):
            assert c == 3
            assert d == 4
            return numpy.ones((5, 5))

    with event_model.Filler({'DUMMY': DummyHandlerRootMapTest},
                            root_map={path_root:
                                      str(tmp_path / "moved")}) as filler:

        filler('start', run_bundle.start_doc)
        filler('descriptor', desc_bundle.descriptor_doc)
        filler('descriptor', desc_bundle_baseline.descriptor_doc)
        filler('resource', res_bundle.resource_doc)
        filler('datum', datum_doc)
        event = copy.deepcopy(raw_event)
        filler('event', event)
        filler('stop', stop_doc)
        assert not filler._closed
    assert event['data']['image'].shape == (5, 5)

    # Test verify_filled.
    with pytest.raises(event_model.UnfilledData):
        event_model.verify_filled(event_model.pack_event_page(raw_event))
    event_model.verify_filled(event_model.pack_event_page(event))