19. SoftSignalBackend Wrapping Arbitrary Callables#
Status#
Accepted
Context#
Users working outside of EPICS/Tango ecosystems such as those wrapping third-party Python APIs, calling analysis scripts, or interfacing with devices with their own Python drivers, currently have no supported path to create signals without writing a full custom SignalBackend. This was identified as a friction point for smaller lab-based groups during the Bluesky community workshop.
To address this, SoftSignalBackend was extended to support arbitrary callables for getting, setting, and polling values. This allows users to integrate external systems without implementing a full backend, reducing boilerplate and improving usability.
Decision#
Extend SoftSignalBackend with Callable Support#
SoftSignalBackend was augmented with three new keyword-only parameters:
getter: A callable (Callable[[], T | Awaitable[T]]) invoked duringget_value()andget_reading()to fetch the current value from an external source. Ifpoll_periodis set, thegetteris called periodically while a subscription is active.setter: A callable (Callable[[SignalDatatypeT], SignalDatatypeT | None | Awaitable[SignalDatatypeT | None]]) invoked duringput(). It may return aSignalDatatypeT; if it returnsNone, thegetter(if configured) is called immediately to refresh the cache.poll_period: A float representing the interval (in seconds) at which thegetteris polled while a subscription is active. Requiresgetterto be set.
Design Choices#
All three parameters are optional. If none are provided, behavior remains identical to the existing
SoftSignalBackend.The internal
self._readingstore remains the single source of truth. Thegetterupdates this store rather than bypassing it, preserving coherence for subscriptions and cached reads.The
putmethod acceptsSignalDatatypeT(the same type as the signal’s stored value) to maintain type safety and consistency.Polling tasks are used for subscriptions, starting in
set_callbackand canceling when subscriptions end.get_setpoint()does not invoke thegetter; it returns the last value written to thesetteror the initial value of.
Factory Function Updates#
The convenience functions soft_signal_rw and soft_signal_r_and_setter were updated to accept getter, and poll_period arguments. soft_signal_rw additionally accepts a setter argument. These additional arguments are passed to SoftSignalBackend.
Consequences#
Improved Usability: Non-EPICS/Tango users can now easily integrate external systems (e.g., Python APIs, scripts) without writing full backends.
No Breaking Changes: Existing code continues to work unchanged since all new parameters are optional.
Consistent Behavior: Polling, caching, and subscriptions align with EPICS/Tango backend patterns.