Closed danielballan closed 5 years ago
Tests added. And here's an interactive "test" / demo:
In [1]: from bluesky import RunEngine
In [2]: from bluesky.plans import count
In [3]: from ophyd.sim import NumpySeqHandler, img
In [4]: from event_model import RunRouter
In [5]: RE = RunEngine({})
In [6]: from suitcase.tiff_stack import Serializer
In [7]: def factory(name, doc):
...: serializer = Serializer('/tmp/test_run_router_fills')
...: serializer(name, doc)
...: return [serializer], []
...:
In [8]: rr = RunRouter([factory], {'NPY_SEQ': NumpySeqHandler})
In [9]: RE.subscribe(rr)
Out[9]: 0
In [10]: RE(count([img]))
Out[10]: ('9da4b611-c89b-40df-a7a6-ec1e766f2bf4',)
In [11]: ls /tmp/test_run_router_fills/
9da4b611-c89b-40df-a7a6-ec1e766f2bf4-primary-img.tiff
I had to rearrange the order that RunRouter
and Filler
are defined in the module in order for RunRouter
to use Filler
is a default value for a parameter. This rearrangement makes it hard to see the actual changes in the full diff. Reviewing this specific commit will make it easier to see the changes.
The coverage complaint is about the moved code, not the new code.
What is the mechanism to use RunRouter without filling? The previous RunRouter didn't fill, and the new RunRouter fills by default. Maybe we shouldn't make RunRouter fill by default so it doesn't break existing code?
Thanks for keeping an eye on backward-compatibility. True, the new RunRouter fills by default if you give it a handler registry. But the default value of handler_registry
is {}
and the default value of fill_or_fail
is False
, so the effect is that it still does not do any filling by default.
In several instances (various demos and some beamlines) we have worked out contorted code to handle the pattern:
where the consumer could be a suitcase or really anyconsumer that requires filled data, like a live image viewer. There is currently no elegant way to do this because
Filler
is a sink: there is no way to "subscribe" to it.One possible solution is to add
Filler.subscribe
, but another, which is better for resource management and also simpler for users, is to putRunRouter
in charge of creating and destroyingFiller
instances. That's what this PR does.Notice that it provides hooks to:
handler_registry
(the simple, common case)DaskFiller
)filler_class=functools.partial(Filler ...)
This was worked out in conversation with @tacaswell.
This needs tests.