Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/fastcs/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ def __init__(
def _link_process_tasks(self):
for controller_api in self.controller_api.walk_api():
_link_put_tasks(controller_api)
_link_attribute_sender_class(controller_api, self._controller)
_link_attribute_sender_class(
controller_api,
self._controller.get_controller_by_path(controller_api.path),
)

def __del__(self):
self._stop_scan_tasks()
Expand Down Expand Up @@ -74,7 +77,7 @@ def _link_put_tasks(controller_api: ControllerAPI) -> None:


def _link_attribute_sender_class(
controller_api: ControllerAPI, controller: Controller
controller_api: ControllerAPI, controller: BaseController
) -> None:
for attr_name, attribute in controller_api.attributes.items():
match attribute:
Expand Down
14 changes: 14 additions & 0 deletions src/fastcs/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import get_type_hints

from fastcs.attributes import Attribute
from fastcs.exceptions import FastCSException


class BaseController:
Expand Down Expand Up @@ -101,6 +102,19 @@ def register_sub_controller(self, name: str, sub_controller: SubController):
def get_sub_controllers(self) -> dict[str, SubController]:
return self.__sub_controller_tree

def __getitem__(self, key: str) -> SubController:
if key not in self.__sub_controller_tree:
raise FastCSException(f"Controller {self} has no sub controller '{key}'")

return self.__sub_controller_tree[key]

def get_controller_by_path(self, path: list[str]) -> BaseController:
if path:
sub_controller, remaining_path = self[path[0]], path[1:]
return sub_controller.get_controller_by_path(remaining_path)

return self


class Controller(BaseController):
"""Top-level controller for a device.
Expand Down
30 changes: 28 additions & 2 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import asyncio
from unittest.mock import call

from pytest_mock import MockerFixture

from fastcs.attributes import AttrRW
from fastcs.backend import Backend, build_controller_api
from fastcs.controller import Controller
from fastcs.controller import Controller, SubController
from fastcs.cs_methods import Command
from fastcs.datatypes import Int
from fastcs.datatypes import Float, Int
from fastcs.wrappers import command, scan


Expand Down Expand Up @@ -36,6 +39,29 @@ async def test_wrapper():
loop.run_until_complete(test_wrapper())


def test_backend_link_attribute_sender(mocker: MockerFixture):
class MyController(Controller):
attr = AttrRW(Float())

class MySubController(SubController):
sub_attr = AttrRW(Int())

controller = MyController()
sub_controller = MySubController()
controller.register_sub_controller("sub", sub_controller)

loop = asyncio.get_event_loop()
create_sender_mock = mocker.patch("fastcs.backend._create_sender_callback")
Backend(controller, loop)

create_sender_mock.assert_has_calls(
[
call(controller.attr, controller),
call(sub_controller.sub_attr, sub_controller),
]
)


def test_controller_api():
class MyTestController(Controller):
attr1: AttrRW[int] = AttrRW(Int())
Expand Down
12 changes: 12 additions & 0 deletions tests/test_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fastcs.attributes import AttrR
from fastcs.controller import Controller, SubController
from fastcs.datatypes import Int
from fastcs.exceptions import FastCSException


def test_controller_nesting():
Expand All @@ -16,7 +17,9 @@ def test_controller_nesting():
assert sub_controller.path == ["a"]
assert sub_sub_controller.path == ["a", "b"]
assert controller.get_sub_controllers() == {"a": sub_controller}
assert controller["a"] == sub_controller
assert sub_controller.get_sub_controllers() == {"b": sub_sub_controller}
assert controller["a"]["b"] == sub_sub_controller

with pytest.raises(
ValueError, match=r"Controller .* already has a SubController registered as .*"
Expand Down Expand Up @@ -112,3 +115,12 @@ class FailingController(SomeController):
),
):
FailingController(SomeSubController())


def test_controller_getitem(controller):
assert controller["SubController01"] == controller.get_sub_controllers().get(
"SubController01"
)

with pytest.raises(FastCSException, match=r"Controller .* has no sub controller.*"):
controller["DoesNotExist"]
Loading