diff --git a/docs/snippets/dynamic.py b/docs/snippets/dynamic.py index 7dde6dfe..f6e6b997 100644 --- a/docs/snippets/dynamic.py +++ b/docs/snippets/dynamic.py @@ -98,9 +98,11 @@ def __init__( index: int, parameters: dict[str, TemperatureControllerParameter], io: TemperatureControllerAttributeIO, + *, + path=None, ): self._parameters = parameters - super().__init__(f"Ramp{index}", ios=[io]) + super().__init__(f"Ramp{index}", ios=[io], path=path) async def initialise(self): for name, attribute in create_attributes(self._parameters).items(): @@ -108,12 +110,12 @@ async def initialise(self): class TemperatureController(Controller): - def __init__(self, settings: IPConnectionSettings): + def __init__(self, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() self._io = TemperatureControllerAttributeIO(self._connection) - super().__init__(ios=[self._io]) + super().__init__(ios=[self._io], path=path) async def connect(self): await self._connection.connect(self._ip_settings) @@ -129,19 +131,19 @@ async def initialise(self): self.add_attribute(name, attribute) for idx, ramp_parameters in enumerate(ramps_api): + name = f"Ramp{idx + 1:02d}" ramp_controller = TemperatureRampController( - idx + 1, ramp_parameters, self._io + idx + 1, ramp_parameters, self._io, path=self.path + [name] ) await ramp_controller.initialise() - self.add_sub_controller(f"Ramp{idx + 1:02d}", ramp_controller) + self.add_sub_controller(name, ramp_controller) await self._connection.close() epics_ca = EpicsCATransport() connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) diff --git a/docs/snippets/static04.py b/docs/snippets/static04.py index 345794ea..d30d055b 100644 --- a/docs/snippets/static04.py +++ b/docs/snippets/static04.py @@ -10,8 +10,7 @@ class TemperatureController(Controller): epics_ca = EpicsCATransport() -controller = TemperatureController() -controller.set_path(["DEMO"]) +controller = TemperatureController(path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static05.py b/docs/snippets/static05.py index 0d3b610a..df6ccc4f 100644 --- a/docs/snippets/static05.py +++ b/docs/snippets/static05.py @@ -14,8 +14,7 @@ class TemperatureController(Controller): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) -controller = TemperatureController() -controller.set_path(["DEMO"]) +controller = TemperatureController(path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static06.py b/docs/snippets/static06.py index 269e3309..9696e98b 100644 --- a/docs/snippets/static06.py +++ b/docs/snippets/static06.py @@ -12,8 +12,8 @@ class TemperatureController(Controller): device_id = AttrR(String()) - def __init__(self, settings: IPConnectionSettings): - super().__init__() + def __init__(self, settings: IPConnectionSettings, *, path=None): + super().__init__(path=path) self._ip_settings = settings self._connection = IPConnection() @@ -25,8 +25,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) diff --git a/docs/snippets/static07.py b/docs/snippets/static07.py index cac5549d..9478c50d 100644 --- a/docs/snippets/static07.py +++ b/docs/snippets/static07.py @@ -34,11 +34,11 @@ async def update(self, attr: AttrR[NumberT, IDAttributeIORef]): class TemperatureController(Controller): device_id = AttrR(String(), io_ref=IDAttributeIORef()) - def __init__(self, settings: IPConnectionSettings): + def __init__(self, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[IDAttributeIO(self._connection)]) + super().__init__(ios=[IDAttributeIO(self._connection)], path=path) async def connect(self): await self._connection.connect(self._ip_settings) @@ -47,8 +47,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static08.py b/docs/snippets/static08.py index 5382fa3c..6370b29a 100644 --- a/docs/snippets/static08.py +++ b/docs/snippets/static08.py @@ -40,11 +40,13 @@ class TemperatureController(Controller): device_id = AttrR(String(), io_ref=TemperatureControllerAttributeIORef("ID")) power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) - def __init__(self, settings: IPConnectionSettings): + def __init__(self, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) async def connect(self): await self._connection.connect(self._ip_settings) @@ -53,8 +55,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static09.py b/docs/snippets/static09.py index dc5bb9d5..de039920 100644 --- a/docs/snippets/static09.py +++ b/docs/snippets/static09.py @@ -47,11 +47,13 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, settings: IPConnectionSettings): + def __init__(self, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) async def connect(self): await self._connection.connect(self._ip_settings) @@ -60,8 +62,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static10.py b/docs/snippets/static10.py index 24f6d523..5b0c5e0d 100644 --- a/docs/snippets/static10.py +++ b/docs/snippets/static10.py @@ -47,10 +47,12 @@ class TemperatureRampController(Controller): start = AttrRW(Int(), io_ref=TemperatureControllerAttributeIORef(name="S")) end = AttrRW(Int(), io_ref=TemperatureControllerAttributeIORef(name="E")) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -59,17 +61,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -78,8 +85,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static11.py b/docs/snippets/static11.py index e07ed5a4..026dfab4 100644 --- a/docs/snippets/static11.py +++ b/docs/snippets/static11.py @@ -54,10 +54,12 @@ class TemperatureRampController(Controller): end = AttrRW(Int(), io_ref=TemperatureControllerAttributeIORef(name="E")) enabled = AttrRW(Enum(OnOffEnum), io_ref=TemperatureControllerAttributeIORef("N")) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -66,17 +68,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -85,8 +92,7 @@ async def connect(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static12.py b/docs/snippets/static12.py index 9f1f2af1..3f1a9abf 100644 --- a/docs/snippets/static12.py +++ b/docs/snippets/static12.py @@ -59,10 +59,12 @@ class TemperatureRampController(Controller): actual = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("A")) voltage = AttrR(Float()) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -71,17 +73,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -98,8 +105,7 @@ async def update_voltages(self): gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static13.py b/docs/snippets/static13.py index b2036c66..a5347175 100644 --- a/docs/snippets/static13.py +++ b/docs/snippets/static13.py @@ -60,10 +60,12 @@ class TemperatureRampController(Controller): actual = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("A")) voltage = AttrR(Float()) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -72,17 +74,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -106,8 +113,7 @@ async def disable_all(self) -> None: gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Temperature Controller") epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static14.py b/docs/snippets/static14.py index f54c93d3..a4a39b2c 100644 --- a/docs/snippets/static14.py +++ b/docs/snippets/static14.py @@ -63,10 +63,12 @@ class TemperatureRampController(Controller): actual = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("A")) voltage = AttrR(Float()) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -75,17 +77,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -113,8 +120,7 @@ async def disable_all(self) -> None: epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) logger.info("Configuring connection settings", connection_settings=connection_settings) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/docs/snippets/static15.py b/docs/snippets/static15.py index aa2d53a9..0ebacc3d 100644 --- a/docs/snippets/static15.py +++ b/docs/snippets/static15.py @@ -66,10 +66,12 @@ class TemperatureRampController(Controller): actual = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("A")) voltage = AttrR(Float()) - def __init__(self, index: int, connection: IPConnection) -> None: + def __init__(self, index: int, connection: IPConnection, *, path=None) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(connection, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(connection, suffix)], + path=path, ) @@ -78,17 +80,22 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef("P")) ramp_rate = AttrRW(Float(), io_ref=TemperatureControllerAttributeIORef("R")) - def __init__(self, ramp_count: int, settings: IPConnectionSettings): + def __init__(self, ramp_count: int, settings: IPConnectionSettings, *, path=None): self._ip_settings = settings self._connection = IPConnection() - super().__init__(ios=[TemperatureControllerAttributeIO(self._connection)]) + super().__init__( + ios=[TemperatureControllerAttributeIO(self._connection)], path=path + ) self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, ramp_count + 1): - controller = TemperatureRampController(index, self._connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self._connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) async def connect(self): await self._connection.connect(self._ip_settings) @@ -116,8 +123,7 @@ async def disable_all(self) -> None: epics_ca = EpicsCATransport(gui=gui_options) connection_settings = IPConnectionSettings("localhost", 25565) logger.info("Configuring connection settings", connection_settings=connection_settings) -controller = TemperatureController(4, connection_settings) -controller.set_path(["DEMO"]) +controller = TemperatureController(4, connection_settings, path=["DEMO"]) fastcs = FastCS(controller, [epics_ca]) if __name__ == "__main__": diff --git a/src/fastcs/controllers/base_controller.py b/src/fastcs/controllers/base_controller.py index 8b29ed6d..380f6466 100755 --- a/src/fastcs/controllers/base_controller.py +++ b/src/fastcs/controllers/base_controller.py @@ -39,9 +39,10 @@ class BaseController(Tracer): def __init__( self, - path: list[str] | None = None, description: str | None = None, ios: Sequence[AnyAttributeIO] | None = None, + *, + path: list[str] | None = None, ) -> None: super().__init__() @@ -49,7 +50,7 @@ def __init__( # Use the argument over the one class defined description. self.description = description - self._path: list[str] = path or [] + self._path: list[str] = list(path) if path else [] # Internal state that should not be accessed directly by base classes self.__attributes: dict[str, Attribute] = {} @@ -287,14 +288,6 @@ def path(self) -> list[str]: """Path prefix of attributes, recursively including parent Controllers.""" return self._path - def set_path(self, path: list[str]): - if self._path: - raise ValueError(f"sub controller is already registered under {self.path}") - - self._path = path - for attribute in self.__attributes.values(): - attribute.set_path(path) - def _check_for_name_clash(self, name: str): namespaces = { "attribute": self.__attributes, @@ -356,7 +349,6 @@ def add_sub_controller(self, name: str, sub_controller: BaseController): f"'{sub_controller.__class__.__name__}'." ) - sub_controller.set_path(self.path + [name]) self.__sub_controllers[name] = sub_controller super().__setattr__(name, sub_controller) @@ -406,14 +398,14 @@ def add_scan(self, name: str, scan: Scan): def scan_methods(self) -> dict[str, Scan]: return self.__scan_methods - def _build_api(self, path: list[str]) -> ControllerAPI: + def _build_api(self) -> ControllerAPI: return ControllerAPI( - path=path, + path=self._path, attributes=self.attributes, command_methods=self.command_methods, scan_methods=self.scan_methods, sub_apis={ - name: sub_controller._build_api(path + [name]) # noqa: SLF001 + name: sub_controller._build_api() # noqa: SLF001 for name, sub_controller in self.sub_controllers.items() }, description=self.description, diff --git a/src/fastcs/controllers/controller.py b/src/fastcs/controllers/controller.py index a6c72602..c95f3fea 100755 --- a/src/fastcs/controllers/controller.py +++ b/src/fastcs/controllers/controller.py @@ -19,8 +19,10 @@ def __init__( self, description: str | None = None, ios: Sequence[AnyAttributeIO] | None = None, + *, + path: list[str] | None = None, ) -> None: - super().__init__(description=description, ios=ios) + super().__init__(description=description, ios=ios, path=path) self._connected = False def add_sub_controller(self, name: str, sub_controller: BaseController): @@ -70,7 +72,7 @@ def create_api_and_tasks( tuple[ControllerAPI, list[ScanCallback], list[ScanCallback]] """ - controller_api = self._build_api(self._path) + controller_api = self._build_api() scan_dict: dict[float, list[ScanCallback]] = defaultdict(list) initial_coros: list[ScanCallback] = [] diff --git a/src/fastcs/controllers/controller_vector.py b/src/fastcs/controllers/controller_vector.py index 11925827..f4e70844 100755 --- a/src/fastcs/controllers/controller_vector.py +++ b/src/fastcs/controllers/controller_vector.py @@ -19,8 +19,10 @@ def __init__( children: Mapping[int, Controller_T], description: str | None = None, ios: Sequence[AnyAttributeIO] | None = None, + *, + path: list[str] | None = None, ) -> None: - super().__init__(description=description, ios=ios) + super().__init__(description=description, ios=ios, path=path) self._children: dict[int, Controller_T] = {} for index, child in children.items(): self[index] = child diff --git a/src/fastcs/demo/controllers.py b/src/fastcs/demo/controllers.py index 5926fc8c..08c5c433 100755 --- a/src/fastcs/demo/controllers.py +++ b/src/fastcs/demo/controllers.py @@ -71,20 +71,29 @@ class TemperatureController(Controller): power = AttrR(Float(), io_ref=TemperatureControllerAttributeIORef(name="P")) voltages = AttrR(Waveform(np.int32, shape=(4,))) - def __init__(self, settings: TemperatureControllerSettings) -> None: + def __init__( + self, + settings: TemperatureControllerSettings, + *, + path: list[str] | None = None, + ) -> None: self.connection = IPConnection() self.suffix = "" super().__init__( - ios=[TemperatureControllerAttributeIO(self.connection, self.suffix)] + ios=[TemperatureControllerAttributeIO(self.connection, self.suffix)], + path=path, ) self._settings = settings self._ramp_controllers: list[TemperatureRampController] = [] for index in range(1, settings.num_ramp_controllers + 1): - controller = TemperatureRampController(index, self.connection) + name = f"R{index}" + controller = TemperatureRampController( + index, self.connection, path=self.path + [name] + ) self._ramp_controllers.append(controller) - self.add_sub_controller(f"R{index}", controller) + self.add_sub_controller(name, controller) @command() async def cancel_all(self) -> None: @@ -138,9 +147,17 @@ class TemperatureRampController(Controller): actual = AttrR(Float(prec=3), io_ref=TemperatureControllerAttributeIORef(name="A")) voltage = AttrR(Float(prec=3)) - def __init__(self, index: int, conn: IPConnection) -> None: + def __init__( + self, + index: int, + conn: IPConnection, + *, + path: list[str] | None = None, + ) -> None: suffix = f"{index:02d}" super().__init__( - f"Ramp{suffix}", ios=[TemperatureControllerAttributeIO(conn, suffix)] + f"Ramp{suffix}", + ios=[TemperatureControllerAttributeIO(conn, suffix)], + path=path, ) self.connection = conn diff --git a/src/fastcs/launch.py b/src/fastcs/launch.py index 7bbe7726..baca0c61 100644 --- a/src/fastcs/launch.py +++ b/src/fastcs/launch.py @@ -198,15 +198,15 @@ def run( def _instantiate_controllers( controllers_options: list[Any], ) -> list[Controller]: - """Instantiate each entry under `controllers:` and seed its path. + """Instantiate each entry under `controllers:` with its path seeded. Each item in ``controllers_options`` is a dynamically-built Pydantic model that exposes ``id``, ``type`` and the controller's options fields inlined as siblings. The originating Controller class and its options-type are looked up in ``_ENTRY_REGISTRY`` (populated by - ``_build_entry_model``). The entry's ``id`` is seeded into the - controller's ``_path`` via ``set_path([id])`` so that - ``ControllerAPI.path`` is rooted at the YAML id. + ``_build_entry_model``). The entry's ``id`` is passed as ``path=[id]`` + to the controller constructor so that ``ControllerAPI.path`` is rooted + at the YAML id. """ seen_ids: set[str] = set() duplicates: list[str] = [] @@ -229,10 +229,11 @@ def _instantiate_controllers( for name in entry_cls.model_fields if name not in ("id", "type") } - controller = registered.cls(registered.options_type(**field_values)) + controller = registered.cls( + registered.options_type(**field_values), path=[entry.id] + ) else: - controller = registered.cls() - controller.set_path([entry.id]) + controller = registered.cls(path=[entry.id]) controllers.append(controller) return controllers diff --git a/tests/assertable_controller.py b/tests/assertable_controller.py index c5791613..3e61432d 100644 --- a/tests/assertable_controller.py +++ b/tests/assertable_controller.py @@ -32,19 +32,20 @@ async def send(self, attr: AttrW[DType_T, MyTestAttributeIORef], value: DType_T) class TestSubController(Controller): read_int: AttrR = AttrR(Int(), io_ref=MyTestAttributeIORef()) - def __init__(self) -> None: - super().__init__(ios=[test_attribute_io]) + def __init__(self, *, path: list[str] | None = None) -> None: + super().__init__(ios=[test_attribute_io], path=path) class MyTestController(Controller): - def __init__(self) -> None: - super().__init__(ios=[test_attribute_io]) + def __init__(self, *, path: list[str] | None = None) -> None: + super().__init__(ios=[test_attribute_io], path=path) self._sub_controllers: list[TestSubController] = [] for index in range(1, 3): - controller = TestSubController() + name = f"SubController{index:02d}" + controller = TestSubController(path=self.path + [name]) self._sub_controllers.append(controller) - self.add_sub_controller(f"SubController{index:02d}", controller) + self.add_sub_controller(name, controller) initialised = False count = 0 @@ -73,7 +74,6 @@ def __init__( self, controller: Controller, mocker: MockerFixture, - path: list[str] | None = None, ) -> None: super().__init__() @@ -81,7 +81,7 @@ def __init__( self.command_method_spys: dict[str, MockType] = {} # Build a ControllerAPI from the given Controller - controller_api = controller._build_api(path or []) + controller_api = controller._build_api() # Copy its fields self.path = controller_api.path self.attributes = controller_api.attributes diff --git a/tests/benchmarking/controller.py b/tests/benchmarking/controller.py index fc2d187e..94f0b60f 100644 --- a/tests/benchmarking/controller.py +++ b/tests/benchmarking/controller.py @@ -21,8 +21,7 @@ def run(): EpicsCATransport(), TangoTransport(), ] - controller = MyTestController() - controller.set_path(["BENCHMARK-DEVICE"]) + controller = MyTestController(path=["BENCHMARK-DEVICE"]) instance = FastCS(controller, transport_options, asyncio.get_event_loop()) instance.run() diff --git a/tests/conftest.py b/tests/conftest.py index 818c7d17..ebd081c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,7 +57,7 @@ def controller(): @pytest.fixture def controller_api(controller): - return controller._build_api([]) + return controller._build_api() DATA_PATH = Path(__file__).parent / "data" diff --git a/tests/example_p4p_ioc.py b/tests/example_p4p_ioc.py index 95cc8e70..bf1494d7 100644 --- a/tests/example_p4p_ioc.py +++ b/tests/example_p4p_ioc.py @@ -43,16 +43,16 @@ class ParentController(Controller): io_ref=SimpleAttributeIORef(), ) - def __init__(self, description=None, ios=None): - super().__init__(description, ios) + def __init__(self, description=None, ios=None, *, path=None): + super().__init__(description, ios, path=path) class ChildController(Controller): fail_on_next_e = True c: AttrW = AttrW(Int(), io_ref=SimpleAttributeIORef()) - def __init__(self, description=None, ios=None): - super().__init__(description, ios) + def __init__(self, description=None, ios=None, *, path=None): + super().__init__(description, ios, path=path) @command() async def d(self): @@ -89,25 +89,30 @@ async def i(self): def run(id="P4P_TEST_DEVICE"): simple_attribute_io = SimpleAttributeIO() p4p_options = EpicsPVATransport() - controller = ParentController(ios=[simple_attribute_io]) - controller.set_path([id]) + controller = ParentController(ios=[simple_attribute_io], path=[id]) class ChildVector(ControllerVector): vector_attribute: AttrR = AttrR(Int()) - def __init__(self, children, description=None): - super().__init__(children, description) + def __init__(self, children, description=None, *, path=None): + super().__init__(children, description, path=path) + child_path = [id, "child"] sub_controller = ChildVector( { 1: ChildController( - description="some sub controller", ios=[simple_attribute_io] + description="some sub controller", + ios=[simple_attribute_io], + path=child_path + ["1"], ), 2: ChildController( - description="another sub controller", ios=[simple_attribute_io] + description="another sub controller", + ios=[simple_attribute_io], + path=child_path + ["2"], ), }, description="some child vector", + path=child_path, ) controller.add_sub_controller("child", sub_controller) diff --git a/tests/example_softioc.py b/tests/example_softioc.py index 5d00ef4d..7749af72 100644 --- a/tests/example_softioc.py +++ b/tests/example_softioc.py @@ -22,9 +22,12 @@ async def d(self): def run(id="SOFTIOC_TEST_DEVICE"): - controller = ParentController() - controller.set_path([id]) - vector = ControllerVector({i: ChildController() for i in range(2)}) + controller = ParentController(path=[id]) + vector_path = [id, "ChildVector"] + vector = ControllerVector( + {i: ChildController(path=vector_path + [str(i)]) for i in range(2)}, + path=vector_path, + ) controller.add_sub_controller("ChildVector", vector) gui_options = EpicsGUIOptions(output_dir=Path("."), title="Demo Vector") fastcs = FastCS( diff --git a/tests/test_controllers.py b/tests/test_controllers.py index 5d5dfb4e..288ab773 100644 --- a/tests/test_controllers.py +++ b/tests/test_controllers.py @@ -11,8 +11,8 @@ def test_controller_nesting(): controller = Controller() - sub_controller = Controller() - sub_sub_controller = Controller() + sub_controller = Controller(path=["a"]) + sub_sub_controller = Controller(path=["a", "b"]) controller.a = sub_controller sub_controller.b = sub_sub_controller @@ -23,15 +23,17 @@ def test_controller_nesting(): assert sub_controller.sub_controllers == {"b": sub_sub_controller} with pytest.raises(ValueError, match=r"Cannot add sub controller"): - controller.a = Controller() + controller.a = Controller(path=["a"]) - with pytest.raises(ValueError, match=r"already registered"): + # sub_controller has path=["a"], so adding it under name "c" violates the + # parent.path + [name] invariant. + with pytest.raises(ValueError, match=r"does not match parent path"): controller.c = sub_controller class SomeSubController(Controller): - def __init__(self): - super().__init__() + def __init__(self, *, path=None): + super().__init__(path=path) sub_attribute = AttrR(Int()) @@ -43,8 +45,8 @@ class SomeController(Controller): equal_attr = AttrR(Int()) annotated_and_equal_attr: AttrR[int] = AttrR(Int()) - def __init__(self, sub_controller: Controller): - super().__init__() + def __init__(self, sub_controller: Controller, *, path=None): + super().__init__(path=path) self.attr_on_object = AttrR(Int()) @@ -55,7 +57,7 @@ def __init__(self, sub_controller: Controller): def test_attribute_parsing(): - sub_controller = SomeSubController() + sub_controller = SomeSubController(path=["sub_controller"]) controller = SomeController(sub_controller) assert set(controller.attributes.keys()) == { @@ -86,13 +88,17 @@ async def noop() -> None: "member_name, member_value, expected_error", [ ("attr", AttrR(Float()), r"Cannot add attribute"), - ("attr", Controller(), r"Cannot add sub controller"), + ("attr", Controller(path=["attr"]), r"Cannot add sub controller"), ("attr", Command(noop), r"Cannot add command"), ("sub_controller", AttrR(Int()), r"Cannot add attribute"), - ("sub_controller", Controller(), r"Cannot add sub controller"), + ( + "sub_controller", + Controller(path=["sub_controller"]), + r"Cannot add sub controller", + ), ("sub_controller", Command(noop), r"Cannot add command"), ("cmd", AttrR(Int()), r"Cannot add attribute"), - ("cmd", Controller(), r"Cannot add sub controller"), + ("cmd", Controller(path=["cmd"]), r"Cannot add sub controller"), ("cmd", Command(noop), r"Cannot add command"), ], ) @@ -105,7 +111,7 @@ class ConflictingController(Controller): def __init__(self): super().__init__() - self.sub_controller = Controller() + self.sub_controller = Controller(path=["sub_controller"]) controller = ConflictingController() @@ -114,7 +120,7 @@ def __init__(self): def test_controller_raises_error_if_passed_numeric_sub_controller_name(): - sub_controller = SomeSubController() + sub_controller = SomeSubController(path=["sub_controller"]) controller = SomeController(sub_controller) with pytest.raises(ValueError, match="Numeric-only names are not allowed"): @@ -122,15 +128,19 @@ def test_controller_raises_error_if_passed_numeric_sub_controller_name(): def test_controller_vector_raises_error_if_add_sub_controller_called(): - controller_vector = ControllerVector({i: SomeSubController() for i in range(2)}) + controller_vector = ControllerVector( + {i: SomeSubController(path=[str(i)]) for i in range(2)} + ) with pytest.raises(NotImplementedError, match="Use __setitem__ instead"): - controller_vector.add_sub_controller("subcontroller", SomeSubController()) + controller_vector.add_sub_controller( + "subcontroller", SomeSubController(path=["subcontroller"]) + ) def test_controller_vector_indexing(): - controller = SomeSubController() - another_controller = SomeSubController() + controller = SomeSubController(path=["10"]) + another_controller = SomeSubController(path=["1"]) controller_vector = ControllerVector({1: another_controller}) controller_vector[10] = controller assert controller_vector.sub_controllers["10"] == controller @@ -142,14 +152,17 @@ def test_controller_vector_indexing(): def test_controller_vector_delitem_raises_exception(): - controller = SomeSubController() + controller = SomeSubController(path=["1"]) controller_vector = ControllerVector({1: controller}) with pytest.raises(NotImplementedError, match="Cannot delete"): del controller_vector[1] def test_controller_vector_iter(): - sub_controllers = {1: SomeSubController(), 2: SomeSubController()} + sub_controllers = { + 1: SomeSubController(path=["1"]), + 2: SomeSubController(path=["2"]), + } controller_vector = ControllerVector(sub_controllers) for index, child in controller_vector.items(): @@ -207,9 +220,9 @@ class HintedController(Controller): controller._validate_type_hints() with pytest.raises(RuntimeError, match="does not match defined type"): - controller.add_sub_controller("child", Controller()) + controller.add_sub_controller("child", Controller(path=["child"])) - controller.add_sub_controller("child", SomeSubController()) + controller.add_sub_controller("child", SomeSubController(path=["child"])) controller._validate_type_hints() @@ -249,7 +262,7 @@ async def scan_nothing(self): pass controller = MyTestController() - api = controller._build_api([]) + api = controller._build_api() assert api.description == controller.description assert list(api.attributes) == ["attr1", "attr2"] diff --git a/tests/test_launch.py b/tests/test_launch.py index 17e935f4..a9a75ebb 100644 --- a/tests/test_launch.py +++ b/tests/test_launch.py @@ -35,37 +35,37 @@ class OtherConfig: class SingleArg(Controller): - def __init__(self): - super().__init__() + def __init__(self, *, path=None): + super().__init__(path=path) class NotHinted(Controller): - def __init__(self, arg): - super().__init__() + def __init__(self, arg, *, path=None): + super().__init__(path=path) class IsHinted(Controller): read = AttrR(Int()) - def __init__(self, arg: SomeConfig) -> None: - super().__init__() + def __init__(self, arg: SomeConfig, *, path=None) -> None: + super().__init__(path=path) class ManyArgs(Controller): - def __init__(self, arg: SomeConfig, too_many): - super().__init__() + def __init__(self, arg: SomeConfig, too_many, *, path=None): + super().__init__(path=path) class OtherHinted(Controller): - def __init__(self, arg: OtherConfig) -> None: - super().__init__() + def __init__(self, arg: OtherConfig, *, path=None) -> None: + super().__init__(path=path) class Aliased(Controller): type_name: ClassVar[str] = "aliased-controller" - def __init__(self, arg: SomeConfig) -> None: - super().__init__() + def __init__(self, arg: SomeConfig, *, path=None) -> None: + super().__init__(path=path) runner = CliRunner() @@ -121,7 +121,7 @@ def test_is_hinted_schema(data): def test_not_hinted_schema(): error = ( "Expected typehinting in 'NotHinted.__init__' but received " - "(self, arg). Add a typehint for `arg`." + "(self, arg, *, path=None). Add a typehint for `arg`." ) with pytest.raises(LaunchError) as exc_info: @@ -133,7 +133,8 @@ def test_over_defined_schema(): error = ( "" "Expected no more than 2 arguments for 'ManyArgs.__init__' " - "but received 3 as `(self, arg: tests.test_launch.SomeConfig, too_many)`" + "but received 3 as `(self, arg: tests.test_launch.SomeConfig, too_many, " + "*, path=None)`" ) with pytest.raises(LaunchError) as exc_info: diff --git a/tests/test_multi_controller.py b/tests/test_multi_controller.py index 9e27f62e..5e1c67f0 100644 --- a/tests/test_multi_controller.py +++ b/tests/test_multi_controller.py @@ -34,10 +34,9 @@ class _OtherAttrController(Controller): def test_controller_api_path_uses_id(): - controller = _IdController() - sub = _IdController() + controller = _IdController(path=["X"]) + sub = _IdController(path=["X", "Sub"]) controller.add_sub_controller("Sub", sub) - controller.set_path(["X"]) api, _, _ = controller.create_api_and_tasks() @@ -46,8 +45,7 @@ def test_controller_api_path_uses_id(): def _api_with_id(controller_class: type[Controller], id: str): - controller = controller_class() - controller.set_path([id]) + controller = controller_class(path=[id]) api, _, _ = controller.create_api_and_tasks() return api @@ -305,8 +303,8 @@ class _LifecycleController(Controller): foo = AttrR(Int()) - def __init__(self): - super().__init__() + def __init__(self, *, path: list[str] | None = None): + super().__init__(path=path) self.connected = False self.initialised = False self.post_initialised = False @@ -332,10 +330,8 @@ class _OtherLifecycleController(_LifecycleController): async def test_fastcs_serves_two_controllers_end_to_end(mocker: MockerFixture): """FastCS.serve drives lifecycle on every controller and routes REST traffic per-id; combined OpenAPI describes both prefixes.""" - a = _LifecycleController() - a.set_path(["alpha"]) - b = _OtherLifecycleController() - b.set_path(["beta"]) + a = _LifecycleController(path=["alpha"]) + b = _OtherLifecycleController(path=["beta"]) transport = RestTransport() # Stop RestTransport from binding to a real port; we exercise the FastAPI diff --git a/tests/transports/epics/ca/test_gui.py b/tests/transports/epics/ca/test_gui.py index 46e000e1..13ad03d1 100644 --- a/tests/transports/epics/ca/test_gui.py +++ b/tests/transports/epics/ca/test_gui.py @@ -102,8 +102,11 @@ def test_get_write_widget_none(): ) -def test_get_components(controller): - controller_api = controller._build_api(["DEVICE"]) +def test_get_components(): + from tests.conftest import BackendTestController + + controller = BackendTestController(path=["DEVICE"]) + controller_api = controller._build_api() gui = EpicsGUI(controller_api) components = gui.extract_api_components(controller_api) @@ -203,8 +206,7 @@ class _B(Controller): def _api_with_id(cls, name): - c = cls() - c.set_path([name]) + c = cls(path=[name]) api, _, _ = c.create_api_and_tasks() return api diff --git a/tests/transports/epics/ca/test_initial_value.py b/tests/transports/epics/ca/test_initial_value.py index b78090dd..44f72767 100644 --- a/tests/transports/epics/ca/test_initial_value.py +++ b/tests/transports/epics/ca/test_initial_value.py @@ -51,8 +51,7 @@ async def test_initial_values_set_in_ca(mocker): pv_prefix = "SOFTIOC_INITIAL_DEVICE" loop = asyncio.get_event_loop() - controller = InitialValuesController() - controller.set_path([pv_prefix]) + controller = InitialValuesController(path=[pv_prefix]) fastcs = FastCS( controller, [EpicsCATransport()], diff --git a/tests/transports/epics/ca/test_softioc.py b/tests/transports/epics/ca/test_softioc.py index 2ad7a601..1beb3cf5 100644 --- a/tests/transports/epics/ca/test_softioc.py +++ b/tests/transports/epics/ca/test_softioc.py @@ -273,7 +273,7 @@ class EpicsController(MyTestController): @pytest.fixture() def epics_controller_api(class_mocker: MockerFixture): - return AssertableControllerAPI(EpicsController(), class_mocker, path=[DEVICE]) + return AssertableControllerAPI(EpicsController(path=[DEVICE]), class_mocker) def test_ioc(mocker: MockerFixture, epics_controller_api: ControllerAPI): @@ -504,7 +504,7 @@ def test_long_pv_names_discarded(mocker: MockerFixture): util_builder = mocker.patch("fastcs.transports.epics.ca.util.builder") ioc_builder = mocker.patch("fastcs.transports.epics.ca.ioc.builder") long_name_controller_api = AssertableControllerAPI( - ControllerLongNames(), mocker, path=[DEVICE] + ControllerLongNames(path=[DEVICE]), mocker ) long_attr_name = "attr_r_with_reallyreallyreallyreallyreallyreallyreally_long_name" long_rw_name = "attr_rw_with_a_reallyreally_long_name_that_is_too_long_for_RBV" diff --git a/tests/transports/epics/pva/test_p4p.py b/tests/transports/epics/pva/test_p4p.py index 947ade79..163df31e 100644 --- a/tests/transports/epics/pva/test_p4p.py +++ b/tests/transports/epics/pva/test_p4p.py @@ -209,8 +209,7 @@ async def test_numeric_alarms(p4p_subprocess: tuple[str, Queue]): a_monitor.close() -def make_fastcs(pv_prefix: str, controller: Controller) -> FastCS: - controller.set_path([pv_prefix]) +def make_fastcs(controller: Controller) -> FastCS: return FastCS(controller, [EpicsPVATransport()]) @@ -219,9 +218,9 @@ class SomeController(Controller): a: AttrRW = AttrRW(Int(max=400_000, max_alarm=40_000)) b: AttrR = AttrR(Float(min=-1, min_alarm=-0.5, prec=2)) - controller = SomeController() pv_prefix = str(uuid4()) - fastcs = make_fastcs(pv_prefix, controller) + controller = SomeController(path=[pv_prefix]) + fastcs = make_fastcs(controller) ctxt = ThreadContext("pva") @@ -272,36 +271,27 @@ class SomeController(Controller): another_attr_1000: AttrRW = AttrRW(Int()) a_third_attr: AttrW = AttrW(Int()) - controller = SomeController() - - sub_controller_vector = ControllerVector({i: ChildController() for i in range(3)}) + # Short id keeps the deepest prefix (`:AdditionalChild:ChildChild`) + # under the 60-char EPICS PV name limit enforced by validate_pva_id. + pv_prefix = uuid4().hex[:8] + controller = SomeController(path=[pv_prefix]) + child_path = [pv_prefix, "child"] + sub_controller_vector = ControllerVector( + {i: ChildController(path=child_path + [str(i)]) for i in range(3)}, + path=child_path, + ) controller.add_sub_controller("child", sub_controller_vector) - sub_controller = ChildController() - controller.child0 = sub_controller - sub_controller.child_child = ChildChildController() - - sub_controller = ChildController() - controller.child1 = sub_controller - sub_controller.child_child = ChildChildController() - - sub_controller = ChildController() - controller.child2 = sub_controller - sub_controller.child_child = ChildChildController() - - sub_controller = ChildController() - controller.another_child = sub_controller - sub_controller.child_child = ChildChildController() - - sub_controller = ChildController() - controller.additional_child = sub_controller - sub_controller.child_child = ChildChildController() + for name in ("child0", "child1", "child2", "another_child", "additional_child"): + sub_path = [pv_prefix, name] + sub_controller = ChildController(path=sub_path) + setattr(controller, name, sub_controller) + sub_controller.child_child = ChildChildController( + path=sub_path + ["child_child"] + ) - # Short id keeps the deepest prefix (`:AdditionalChild:ChildChild`) - # under the 60-char EPICS PV name limit enforced by validate_pva_id. - pv_prefix = uuid4().hex[:8] - fastcs = make_fastcs(pv_prefix, controller) + fastcs = make_fastcs(controller) ctxt = ThreadContext("pva") @@ -411,9 +401,9 @@ class SomeController(Controller): some_table: AttrRW = AttrRW(Table(table_columns)) some_enum: AttrRW = AttrRW(Enum(AnEnum)) - controller = SomeController() pv_prefix = str(uuid4()) - fastcs = make_fastcs(pv_prefix, controller) + controller = SomeController(path=[pv_prefix]) + fastcs = make_fastcs(controller) ctxt = ThreadContext("pva", nt=False) @@ -550,9 +540,9 @@ async def some_task(): asyncio.create_task(some_task()) - controller = SomeController() pv_prefix = str(uuid4()) - fastcs = make_fastcs(pv_prefix, controller) + controller = SomeController(path=[pv_prefix]) + fastcs = make_fastcs(controller) expected_error_string = ( "RuntimeError: Received request to run command but it is " "already in progress. Maybe the command should spawn an asyncio task?" @@ -619,9 +609,9 @@ class SomeController(Controller): async def command_runs_for_a_while(self): await asyncio.sleep(0.2) - controller = SomeController() pv_prefix = str(uuid4()) - fastcs = make_fastcs(pv_prefix, controller) + controller = SomeController(path=[pv_prefix]) + fastcs = make_fastcs(controller) command_runs_for_a_while_times = [] async def put_pvs(): diff --git a/tests/transports/epics/test_emission.py b/tests/transports/epics/test_emission.py index b4e22365..f1164277 100644 --- a/tests/transports/epics/test_emission.py +++ b/tests/transports/epics/test_emission.py @@ -32,8 +32,7 @@ class _Beta(Controller): def _api_with_id(controller_class: type[Controller], name: str): - controller = controller_class() - controller.set_path([name]) + controller = controller_class(path=[name]) api, _, _ = controller.create_api_and_tasks() return api diff --git a/tests/transports/graphQL/test_graphql.py b/tests/transports/graphQL/test_graphql.py index 46d2fc8a..1fd66acc 100644 --- a/tests/transports/graphQL/test_graphql.py +++ b/tests/transports/graphQL/test_graphql.py @@ -31,7 +31,7 @@ class GraphQLController(MyTestController): @pytest.fixture(scope="class") def gql_controller_api(class_mocker: MockerFixture): - return AssertableControllerAPI(GraphQLController(), class_mocker, path=[_GQL_ID]) + return AssertableControllerAPI(GraphQLController(path=[_GQL_ID]), class_mocker) def nest_query(path: list[str]) -> str: diff --git a/tests/transports/tango/test_dsr.py b/tests/transports/tango/test_dsr.py index 61a8f73e..3e7dfd74 100644 --- a/tests/transports/tango/test_dsr.py +++ b/tests/transports/tango/test_dsr.py @@ -43,7 +43,7 @@ class TangoController(MyTestController): @pytest.fixture(scope="class") def tango_controller_api(class_mocker: MockerFixture) -> AssertableControllerAPI: - return AssertableControllerAPI(TangoController(), class_mocker, path=["DEVICE"]) + return AssertableControllerAPI(TangoController(path=["DEVICE"]), class_mocker) def create_test_context(tango_controller_api: AssertableControllerAPI):