From bc4e98b1eb04796b23668210153bf3a57ec583a3 Mon Sep 17 00:00:00 2001 From: James Souter Date: Thu, 18 Apr 2024 14:24:41 +0100 Subject: [PATCH] Make backend source a method. signals can pass a value to the backend, signal sources are now properties --- src/ophyd_async/core/signal.py | 17 +++++++++-------- src/ophyd_async/core/signal_backend.py | 7 +++++-- src/ophyd_async/core/sim_signal_backend.py | 11 +++++------ src/ophyd_async/epics/_backend/_aioca.py | 8 +++++--- src/ophyd_async/epics/_backend/_p4p.py | 9 ++++++--- src/ophyd_async/epics/pvi/pvi.py | 6 +++--- tests/core/test_flyer.py | 4 ++-- tests/core/test_signal.py | 19 +++++++++---------- tests/core/test_sim.py | 15 ++++++++------- tests/core/test_utils.py | 2 +- .../areadetector/test_single_trigger_det.py | 4 ---- tests/epics/areadetector/test_writers.py | 2 +- tests/epics/demo/test_demo.py | 9 --------- tests/epics/demo/test_demo_ad_sim_detector.py | 5 ++--- tests/epics/motion/test_motor.py | 6 ------ tests/epics/test_signals.py | 15 +++++++-------- tests/test_flyer_with_panda.py | 4 ++-- 17 files changed, 65 insertions(+), 78 deletions(-) diff --git a/src/ophyd_async/core/signal.py b/src/ophyd_async/core/signal.py index 2e471a4ba1..a9c76af3f4 100644 --- a/src/ophyd_async/core/signal.py +++ b/src/ophyd_async/core/signal.py @@ -61,7 +61,7 @@ def set_name(self, name: str = ""): async def connect(self, sim=False, timeout=DEFAULT_TIMEOUT): if sim: self._backend = SimSignalBackend( - datatype=self._init_backend.datatype, source=self._init_backend.source + datatype=self._init_backend.datatype ) _sim_backends[self] = self._backend else: @@ -72,7 +72,7 @@ async def connect(self, sim=False, timeout=DEFAULT_TIMEOUT): @property def source(self) -> str: """Like ca://PV_PREFIX:SIGNAL, or "" if not set""" - return self._backend.source + return self._backend.source(self.name) __lt__ = __le__ = __eq__ = __ge__ = __gt__ = __ne__ = _fail @@ -168,7 +168,7 @@ async def read(self, cached: Optional[bool] = None) -> Dict[str, Reading]: @_add_timeout async def describe(self) -> Dict[str, Descriptor]: """Return a single item dict with the descriptor in it""" - return {self.name: await self._backend.get_descriptor()} + return {self.name: await self._backend.get_descriptor(self.source)} @_add_timeout async def get_value(self, cached: Optional[bool] = None) -> T: @@ -256,27 +256,28 @@ def set_sim_callback(signal: Signal[T], callback: ReadingValueCallback[T]) -> No def soft_signal_rw( datatype: Optional[Type[T]], name: str, - source_prefix: str, initial_value: Optional[T] = None, ) -> SignalRW[T]: """Creates a read-writable Signal with a SimSignalBackend""" - return SignalRW( - SimSignalBackend(datatype, f"sim://{source_prefix}:{name}", initial_value) + signal = SignalRW( + SimSignalBackend(datatype, initial_value) ) + signal.set_name(name) + return signal def soft_signal_r_and_backend( datatype: Optional[Type[T]], name: str, - source_prefix: str, initial_value: Optional[T] = None, ) -> Tuple[SignalR[T], SimSignalBackend]: """Returns a tuple of a read-only Signal and its SimSignalBackend through which the signal can be internally modified within the device. Use soft_signal_rw if you want a device that is externally modifiable """ - backend = SimSignalBackend(datatype, f"sim://{source_prefix}:{name}", initial_value) + backend = SimSignalBackend(datatype, initial_value) signal = SignalR(backend) + signal.set_name(name) return (signal, backend) diff --git a/src/ophyd_async/core/signal_backend.py b/src/ophyd_async/core/signal_backend.py index 778e62c76c..95d0ca1768 100644 --- a/src/ophyd_async/core/signal_backend.py +++ b/src/ophyd_async/core/signal_backend.py @@ -13,7 +13,10 @@ class SignalBackend(Generic[T]): datatype: Optional[Type[T]] = None #: Like ca://PV_PREFIX:SIGNAL - source: str = "" + @abstractmethod + def source(name: str) -> str: + """Return source of signal. Signals may pass a name to the backend, which can be + used or discarded.""" @abstractmethod async def connect(self, timeout: float = DEFAULT_TIMEOUT): @@ -24,7 +27,7 @@ async def put(self, value: Optional[T], wait=True, timeout=None): """Put a value to the PV, if wait then wait for completion for up to timeout""" @abstractmethod - async def get_descriptor(self) -> Descriptor: + async def get_descriptor(self, source: str) -> Descriptor: """Metadata like source, dtype, shape, precision, units""" @abstractmethod diff --git a/src/ophyd_async/core/sim_signal_backend.py b/src/ophyd_async/core/sim_signal_backend.py index 9a176b826b..81d079f3d8 100644 --- a/src/ophyd_async/core/sim_signal_backend.py +++ b/src/ophyd_async/core/sim_signal_backend.py @@ -116,19 +116,18 @@ class SimSignalBackend(SignalBackend[T]): def __init__( self, datatype: Optional[Type[T]], - source: str, initial_value: Optional[T] = None, ) -> None: - pv = re.split(r"://", source)[-1] - self.source = f"sim://{pv}" self.datatype = datatype - self.pv = source self.converter: SimConverter = DisconnectedSimConverter() self._initial_value = initial_value self.put_proceeds = asyncio.Event() self.put_proceeds.set() self.callback: Optional[ReadingValueCallback[T]] = None + def source(self, name: str) -> str: + return f"soft://{name}" + async def connect(self, timeout: float = DEFAULT_TIMEOUT) -> None: self.converter = make_converter(self.datatype) if self._initial_value is None: @@ -162,8 +161,8 @@ def _set_value(self, value: T): if self.callback: self.callback(reading, self._value) - async def get_descriptor(self) -> Descriptor: - return self.converter.descriptor(self.source, self._value) + async def get_descriptor(self, source: str) -> Descriptor: + return self.converter.descriptor(source, self._value) async def get_reading(self) -> Reading: return self.converter.reading(self._value, self._timestamp, self._severity) diff --git a/src/ophyd_async/epics/_backend/_aioca.py b/src/ophyd_async/epics/_backend/_aioca.py index 00cb0cbfc3..a4e5f81dcd 100644 --- a/src/ophyd_async/epics/_backend/_aioca.py +++ b/src/ophyd_async/epics/_backend/_aioca.py @@ -170,9 +170,11 @@ def __init__(self, datatype: Optional[Type[T]], read_pv: str, write_pv: str): self.write_pv = write_pv self.initial_values: Dict[str, AugmentedValue] = {} self.converter: CaConverter = DisconnectedCaConverter(None, None) - self.source = f"ca://{self.read_pv}" self.subscription: Optional[Subscription] = None + def source(self, name: str): + return f"ca://{self.read_pv}" + async def _store_initial_value(self, pv, timeout: float = DEFAULT_TIMEOUT): try: self.initial_values[pv] = await caget( @@ -216,9 +218,9 @@ async def _caget(self, format: Format) -> AugmentedValue: timeout=None, ) - async def get_descriptor(self) -> Descriptor: + async def get_descriptor(self, source: str) -> Descriptor: value = await self._caget(FORMAT_CTRL) - return self.converter.descriptor(self.source, value) + return self.converter.descriptor(source, value) async def get_reading(self) -> Reading: value = await self._caget(FORMAT_TIME) diff --git a/src/ophyd_async/epics/_backend/_p4p.py b/src/ophyd_async/epics/_backend/_p4p.py index 0507ff4d32..801bc67bf3 100644 --- a/src/ophyd_async/epics/_backend/_p4p.py +++ b/src/ophyd_async/epics/_backend/_p4p.py @@ -236,9 +236,12 @@ def __init__(self, datatype: Optional[Type[T]], read_pv: str, write_pv: str): self.write_pv = write_pv self.initial_values: Dict[str, Any] = {} self.converter: PvaConverter = DisconnectedPvaConverter() - self.source = f"pva://{self.read_pv}" self.subscription: Optional[Subscription] = None + @property + def source(self, name: str): + return f"pva://{self.read_pv}" + @property def ctxt(self) -> Context: if PvaSignalBackend._ctxt is None: @@ -290,9 +293,9 @@ async def put(self, value: Optional[T], wait=True, timeout=None): ) raise NotConnected(f"pva://{self.write_pv}") from exc - async def get_descriptor(self) -> Descriptor: + async def get_descriptor(self, source: str) -> Descriptor: value = await self.ctxt.get(self.read_pv) - return self.converter.descriptor(self.source, value) + return self.converter.descriptor(source, value) def _pva_request_string(self, fields: List[str]) -> str: """ diff --git a/src/ophyd_async/epics/pvi/pvi.py b/src/ophyd_async/epics/pvi/pvi.py index dc211250db..e290d7ddec 100644 --- a/src/ophyd_async/epics/pvi/pvi.py +++ b/src/ophyd_async/epics/pvi/pvi.py @@ -163,8 +163,8 @@ def _sim_common_blocks(device: Device, stripped_type: Optional[Type] = None): if is_device_vector: if is_signal: signal_type = args[0] if (args := get_args(sub_device_t)) else None - sub_device_1 = sub_device_t(SimSignalBackend(signal_type, sub_name)) - sub_device_2 = sub_device_t(SimSignalBackend(signal_type, sub_name)) + sub_device_1 = sub_device_t(SimSignalBackend(signal_type)) + sub_device_2 = sub_device_t(SimSignalBackend(signal_type)) sub_device = DeviceVector( { 1: sub_device_1, @@ -183,7 +183,7 @@ def _sim_common_blocks(device: Device, stripped_type: Optional[Type] = None): elif is_signal: signal_type = args[0] if (args := get_args(sub_device_t)) else None - sub_device = sub_device_t(SimSignalBackend(signal_type, sub_name)) + sub_device = sub_device_t(SimSignalBackend(signal_type)) else: sub_device = sub_device_t() diff --git a/tests/core/test_flyer.py b/tests/core/test_flyer.py index 747850f846..53238a9578 100644 --- a/tests/core/test_flyer.py +++ b/tests/core/test_flyer.py @@ -51,7 +51,7 @@ async def stop(self): class DummyWriter(DetectorWriter): def __init__(self, name: str, shape: Sequence[int]): - self.dummy_signal = SignalRW(backend=SimSignalBackend(int, source="test")) + self.dummy_signal = SignalRW(backend=SimSignalBackend(int)) self._shape = shape self._name = name self._file: Optional[ComposeStreamResourceBundle] = None @@ -61,7 +61,7 @@ def __init__(self, name: str, shape: Sequence[int]): async def open(self, multiplier: int = 1) -> Dict[str, Descriptor]: return { self._name: Descriptor( - source="sim://some-source", + source="soft://some-source", shape=self._shape, dtype="number", external="STREAM:", diff --git a/tests/core/test_signal.py b/tests/core/test_signal.py index 3b16601ddd..6796248a06 100644 --- a/tests/core/test_signal.py +++ b/tests/core/test_signal.py @@ -29,7 +29,7 @@ async def connect(self, sim=False, timeout=DEFAULT_TIMEOUT): def test_signals_equality_raises(): - sim_backend = SimSignalBackend(str, "test") + sim_backend = SimSignalBackend(str) s1 = MySignal(sim_backend) s2 = MySignal(sim_backend) @@ -48,7 +48,7 @@ def test_signals_equality_raises(): async def test_set_sim_put_proceeds(): - sim_signal = Signal(SimSignalBackend(str, "test")) + sim_signal = Signal(SimSignalBackend(str)) await sim_signal.connect(sim=True) assert sim_signal._backend.put_proceeds.is_set() is True @@ -66,7 +66,7 @@ async def time_taken_by(coro) -> float: async def test_wait_for_value_with_value(): - sim_signal = SignalRW(SimSignalBackend(str, "test")) + sim_signal = SignalRW(SimSignalBackend(str)) sim_signal.set_name("sim_signal") await sim_signal.connect(sim=True) set_sim_value(sim_signal, "blah") @@ -87,7 +87,7 @@ async def test_wait_for_value_with_value(): async def test_wait_for_value_with_funcion(): - sim_signal = SignalRW(SimSignalBackend(float, "test")) + sim_signal = SignalRW(SimSignalBackend(float)) sim_signal.set_name("sim_signal") await sim_signal.connect(sim=True) set_sim_value(sim_signal, 45.8) @@ -113,7 +113,7 @@ def less_than_42(v): async def test_set_and_wait_for_value(): - sim_signal = SignalRW(SimSignalBackend(int, "test")) + sim_signal = SignalRW(SimSignalBackend(int)) sim_signal.set_name("sim_signal") await sim_signal.connect(sim=True) set_sim_value(sim_signal, 0) @@ -129,15 +129,14 @@ async def test_set_and_wait_for_value(): [(soft_signal_r_and_backend, SignalR), (soft_signal_rw, SignalRW)], ) async def test_create_soft_signal(signal_method, signal_class): - TEST_PREFIX = "TEST-PREFIX" - SIGNAL_NAME = "SIGNAL" + SIGNAL_NAME = "TEST-PREFIX:SIGNAL" INITIAL_VALUE = "INITIAL" if signal_method == soft_signal_r_and_backend: - signal, backend = signal_method(str, SIGNAL_NAME, TEST_PREFIX, INITIAL_VALUE) + signal, backend = signal_method(str, SIGNAL_NAME, INITIAL_VALUE) elif signal_method == soft_signal_rw: - signal = signal_method(str, SIGNAL_NAME, TEST_PREFIX, INITIAL_VALUE) + signal = signal_method(str, SIGNAL_NAME, INITIAL_VALUE) backend = signal._backend - assert signal._backend.source == f"sim://{TEST_PREFIX}:{SIGNAL_NAME}" + assert signal.source == f"soft://{SIGNAL_NAME}" assert isinstance(signal, signal_class) assert isinstance(signal._backend, SimSignalBackend) await signal.connect() diff --git a/tests/core/test_sim.py b/tests/core/test_sim.py index a7459b9429..d9b262ed31 100644 --- a/tests/core/test_sim.py +++ b/tests/core/test_sim.py @@ -93,15 +93,16 @@ async def test_backend_get_put_monitor( put_value: T, descriptor: Callable[[Any], dict], ): - backend = SimSignalBackend(datatype, "") + backend = SimSignalBackend(datatype) await backend.connect() q = MonitorQueue(backend) try: # Check descriptor + source = "soft://test" assert ( - dict(source="sim://", **descriptor(initial_value)) - == await backend.get_descriptor() + dict(source=source, **descriptor(initial_value)) + == await backend.get_descriptor(source) ) # Check initial value await q.assert_updates( @@ -115,13 +116,13 @@ async def test_backend_get_put_monitor( async def test_sim_backend_if_disconnected(): - sim_backend = SimSignalBackend(npt.NDArray[np.float64], "SOME-IOC:PV") + sim_backend = SimSignalBackend(npt.NDArray[np.float64]) with pytest.raises(NotImplementedError): await sim_backend.get_value() async def test_sim_backend_with_numpy_typing(): - sim_backend = SimSignalBackend(npt.NDArray[np.float64], "SOME-IOC:PV") + sim_backend = SimSignalBackend(npt.NDArray[np.float64]) await sim_backend.connect() array = await sim_backend.get_value() @@ -133,8 +134,8 @@ class myClass: def __init__(self) -> None: pass - sim_signal = Signal(SimSignalBackend(myClass, "test")) + sim_signal = Signal(SimSignalBackend(myClass)) await sim_signal.connect(sim=True) with pytest.raises(AssertionError): - await sim_signal._backend.get_descriptor() + await sim_signal._backend.get_descriptor("") diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 59dd8cd3f0..7c70ac225e 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -24,7 +24,7 @@ async def connect(self, timeout: float = DEFAULT_TIMEOUT): class WorkingDummyChildDevice(Device): def __init__(self, name: str = "working_dummy_child_device") -> None: - self.working_signal = SignalRW(backend=SimSignalBackend(int, "WORKING_SIGNAL")) + self.working_signal = SignalRW(backend=SimSignalBackend(int)) super().__init__(name=name) diff --git a/tests/epics/areadetector/test_single_trigger_det.py b/tests/epics/areadetector/test_single_trigger_det.py index 14d0ea06e8..684f72c3cf 100644 --- a/tests/epics/areadetector/test_single_trigger_det.py +++ b/tests/epics/areadetector/test_single_trigger_det.py @@ -44,9 +44,5 @@ async def test_single_trigger_det(single_trigger_det: SingleTriggerDet, RE: RunE assert names == ["start", "descriptor", "event", "stop"] _, descriptor, event, _ = docs assert descriptor["configuration"]["det"]["data"]["det-drv-acquire_time"] == 0.5 - assert ( - descriptor["data_keys"]["det-stats-unique_id"]["source"] - == "sim://PREFIX:STATSUniqueId_RBV" - ) assert event["data"]["det-drv-array_counter"] == 1 assert event["data"]["det-stats-unique_id"] == 3 diff --git a/tests/epics/areadetector/test_writers.py b/tests/epics/areadetector/test_writers.py index 0c9f4eb092..6eabab6ed6 100644 --- a/tests/epics/areadetector/test_writers.py +++ b/tests/epics/areadetector/test_writers.py @@ -40,7 +40,7 @@ async def test_correct_descriptor_doc_after_open(hdf_writer: HDFWriter): assert descriptor == { "test": { - "source": "sim://HDF:FullFileName_RBV", + "source": "soft://hdf-full_file_name", "shape": (10, 10), "dtype": "array", "external": "STREAM:", diff --git a/tests/epics/demo/test_demo.py b/tests/epics/demo/test_demo.py index 5b8847e842..37f569a1e3 100644 --- a/tests/epics/demo/test_demo.py +++ b/tests/epics/demo/test_demo.py @@ -131,9 +131,6 @@ async def test_mover_stopped(sim_mover: demo.Mover): async def test_read_mover(sim_mover: demo.Mover): await sim_mover.stage() assert (await sim_mover.read())["sim_mover"]["value"] == 0.0 - assert (await sim_mover.describe())["sim_mover"][ - "source" - ] == "sim://BLxxI-MO-TABLE-01:X:Readback" assert (await sim_mover.read_configuration())["sim_mover-velocity"]["value"] == 1 assert (await sim_mover.describe_configuration())["sim_mover-units"]["shape"] == [] set_sim_value(sim_mover.readback, 0.5) @@ -147,9 +144,6 @@ async def test_read_mover(sim_mover: demo.Mover): async def test_set_velocity(sim_mover: demo.Mover) -> None: v = sim_mover.velocity - assert (await v.describe())["sim_mover-velocity"][ - "source" - ] == "sim://BLxxI-MO-TABLE-01:X:Velocity" q: asyncio.Queue[Dict[str, Reading]] = asyncio.Queue() v.subscribe(q.put_nowait) assert (await q.get())["sim_mover-velocity"]["value"] == 1.0 @@ -184,9 +178,6 @@ async def test_sensor_disconnected(caplog): async def test_read_sensor(sim_sensor: demo.Sensor): sim_sensor.stage() assert (await sim_sensor.read())["sim_sensor-value"]["value"] == 0 - assert (await sim_sensor.describe())["sim_sensor-value"][ - "source" - ] == "sim://SIM:SENSOR:Value" assert (await sim_sensor.read_configuration())["sim_sensor-mode"][ "value" ] == demo.EnergyMode.low diff --git a/tests/epics/demo/test_demo_ad_sim_detector.py b/tests/epics/demo/test_demo_ad_sim_detector.py index 241f974dec..32e0908de0 100644 --- a/tests/epics/demo/test_demo_ad_sim_detector.py +++ b/tests/epics/demo/test_demo_ad_sim_detector.py @@ -212,15 +212,14 @@ async def test_detector_writes_to_file( async def test_read_and_describe_detector(single_detector: StandardDetector): describe = await single_detector.describe_configuration() read = await single_detector.read_configuration() - assert describe == { "test-drv-acquire_time": { - "source": "sim://TEST:DRV:AcquireTime_RBV", + "source": "soft://test-drv-acquire_time", "dtype": "number", "shape": [], }, "test-drv-acquire": { - "source": "sim://TEST:DRV:Acquire_RBV", + "source": "soft://test-drv-acquire", "dtype": "boolean", "shape": [], }, diff --git a/tests/epics/motion/test_motor.py b/tests/epics/motion/test_motor.py index 7706099295..24972fcfcd 100644 --- a/tests/epics/motion/test_motor.py +++ b/tests/epics/motion/test_motor.py @@ -81,9 +81,6 @@ async def test_motor_moving_stopped(sim_motor: motor.Motor): async def test_read_motor(sim_motor: motor.Motor): sim_motor.stage() assert (await sim_motor.read())["sim_motor"]["value"] == 0.0 - assert (await sim_motor.describe())["sim_motor"][ - "source" - ] == "sim://BLxxI-MO-TABLE-01:X.RBV" assert (await sim_motor.read_configuration())["sim_motor-velocity"]["value"] == 1 assert (await sim_motor.describe_configuration())["sim_motor-units"]["shape"] == [] set_sim_value(sim_motor.readback, 0.5) @@ -97,9 +94,6 @@ async def test_read_motor(sim_motor: motor.Motor): async def test_set_velocity(sim_motor: motor.Motor) -> None: v = sim_motor.velocity - assert (await v.describe())["sim_motor-velocity"][ - "source" - ] == "sim://BLxxI-MO-TABLE-01:X.VELO" q: asyncio.Queue[Dict[str, Reading]] = asyncio.Queue() v.subscribe(q.put_nowait) assert (await q.get())["sim_motor-velocity"]["value"] == 1.0 diff --git a/tests/epics/test_signals.py b/tests/epics/test_signals.py index 2c00bde508..c10c8a2d6f 100644 --- a/tests/epics/test_signals.py +++ b/tests/epics/test_signals.py @@ -125,8 +125,8 @@ async def assert_monitor_then_put( q = MonitorQueue(backend) try: # Check descriptor - source = f"{ioc.protocol}://{PV_PREFIX}:{ioc.protocol}:{suffix}" - assert dict(source=source, **descriptor) == await backend.get_descriptor() + pv_name = f"{ioc.protocol}://{PV_PREFIX}:{ioc.protocol}:{suffix}" + assert dict(source=pv_name, **descriptor) == await backend.get_descriptor(pv_name) # Check initial value await q.assert_updates(pytest.approx(initial_value)) # Put to new value and check that @@ -397,7 +397,7 @@ async def test_pva_table(ioc: IOC) -> None: q = MonitorQueue(backend) try: # Check descriptor - dict(source=backend.source, **descriptor) == await backend.get_descriptor() + dict(source="test-source", **descriptor) == await backend.get_descriptor("test-source") # Check initial value await q.assert_updates(approx_table(i)) # Put to new value and check that @@ -432,7 +432,7 @@ async def test_pvi_structure(ioc: IOC) -> None: try: # Check descriptor with pytest.raises(NotImplementedError): - await backend.get_descriptor() + await backend.get_descriptor("") # Check initial value await q.assert_updates(expected) await backend.get_value() @@ -459,10 +459,10 @@ async def test_pva_ntdarray(ioc: IOC): for i, p in [(initial, put), (put, initial)]: with closing(MonitorQueue(backend)) as q: assert { - "source": backend.source, + "source": "test-source", "dtype": "array", "shape": [2, 3], - } == await backend.get_descriptor() + } == await backend.get_descriptor("test-source") # Check initial value await q.assert_updates(pytest.approx(i)) await raw_data_backend.put(p.flatten()) @@ -484,8 +484,7 @@ async def test_non_existent_errors(ioc: IOC): backend = await ioc.make_backend(str, "non-existent", connect=False) # Can't use asyncio.wait_for on python3.8 because of # https://github.com/python/cpython/issues/84787 - - with pytest.raises(NotConnected, match=backend.source): + with pytest.raises(NotConnected): await backend.connect(timeout=0.1) diff --git a/tests/test_flyer_with_panda.py b/tests/test_flyer_with_panda.py index 573177e701..f32067027d 100644 --- a/tests/test_flyer_with_panda.py +++ b/tests/test_flyer_with_panda.py @@ -28,7 +28,7 @@ class DummyWriter(DetectorWriter): def __init__(self, name: str, shape: Sequence[int]): - self.dummy_signal = SignalRW(backend=SimSignalBackend(int, source="test")) + self.dummy_signal = SignalRW(backend=SimSignalBackend(int)) self._shape = shape self._name = name self._file: Optional[ComposeStreamResourceBundle] = None @@ -38,7 +38,7 @@ def __init__(self, name: str, shape: Sequence[int]): async def open(self, multiplier: int = 1) -> Dict[str, Descriptor]: return { self._name: Descriptor( - source="sim://some-source", + source="soft://some-source", shape=self._shape, dtype="number", external="STREAM:",