From 4d0974359b4320fc96bef2a3024270363ca174a3 Mon Sep 17 00:00:00 2001 From: Eva Lott Date: Mon, 2 Dec 2024 11:21:53 +0000 Subject: [PATCH] Added `root_attribute` `root_attribute` is parsed on `register_sub_controller` --- src/fastcs/controller.py | 19 ++++++++++++++++--- tests/test_controller.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/fastcs/controller.py b/src/fastcs/controller.py index 13c27792..5dfac18f 100755 --- a/src/fastcs/controller.py +++ b/src/fastcs/controller.py @@ -27,7 +27,7 @@ def __init__(self, path: list[str] | None = None) -> None: if not hasattr(self, "attributes"): self.attributes = {} self._path: list[str] = path or [] - self.__sub_controller_tree: dict[str, BaseController] = {} + self.__sub_controller_tree: dict[str, SubController] = {} self._bind_attrs() @@ -60,7 +60,8 @@ def _bind_attrs(self) -> None: new_attribute = copy(attr) setattr(self, attr_name, new_attribute) - self.attributes[attr_name] = new_attribute + if attr_name != "root_attribute": + self.attributes[attr_name] = new_attribute def register_sub_controller(self, name: str, sub_controller: SubController): if name in self.__sub_controller_tree.keys(): @@ -71,7 +72,16 @@ def register_sub_controller(self, name: str, sub_controller: SubController): self.__sub_controller_tree[name] = sub_controller sub_controller.set_path(self.path + [name]) - def get_sub_controllers(self) -> dict[str, BaseController]: + if isinstance(sub_controller.root_attribute, Attribute): + if name in self.attributes: + raise TypeError( + f"Cannot set SubController `{name}` root attribute " + f"on the parent controller `{type(self).__name__}` " + f"as it already has an attribute of that name." + ) + self.attributes[name] = sub_controller.root_attribute + + def get_sub_controllers(self) -> dict[str, SubController]: return self.__sub_controller_tree def get_controller_mappings(self) -> list[SingleMapping]: @@ -103,6 +113,7 @@ def _get_single_mapping(controller: BaseController) -> SingleMapping: for name, attribute in controller.attributes.items() if attribute.enabled } + return SingleMapping( controller, scan_methods, put_methods, command_methods, enabled_attributes ) @@ -134,5 +145,7 @@ class SubController(BaseController): it as part of a larger device. """ + root_attribute: Attribute | None = None + def __init__(self) -> None: super().__init__() diff --git a/tests/test_controller.py b/tests/test_controller.py index 866ed66e..b404f641 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -91,3 +91,36 @@ def test_attribute_parsing(): assert sub_controller_mapping.attributes == { "sub_attribute": sub_controller.sub_attribute, } + + +def test_attribute_in_both_class_and_get_attributes(): + class FailingController(Controller): + duplicate_attribute = AttrR(Int()) + + def __init__(self): + self.attributes = {"duplicate_attribute": AttrR(Int())} + super().__init__() + + with pytest.raises( + ValueError, + match=( + "`FailingController` has conflicting attribute `duplicate_attribute` " + "already present in the attributes dict." + ), + ): + FailingController() + + +def test_root_attribute(): + class FailingController(SomeController): + sub_controller = AttrR(Int()) + + with pytest.raises( + TypeError, + match=( + "Cannot set SubController `sub_controller` root attribute " + "on the parent controller `FailingController` as it already " + "has an attribute of that name." + ), + ): + next(_walk_mappings(FailingController(SomeSubController())))