Skip to content

Commit 5243d6a

Browse files
committed
Added root_attribute
`root_attribute` is parsed on `register_sub_controller`
1 parent eee3951 commit 5243d6a

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

src/fastcs/controller.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ class SingleMapping:
2020

2121

2222
class BaseController:
23-
#! Attributes passed from the device at runtime.
23+
#: Attributes passed from the device at runtime.
2424
attributes: dict[str, Attribute]
2525

2626
def __init__(self, path: list[str] | None = None) -> None:
2727
if not hasattr(self, "attributes"):
2828
self.attributes = {}
2929
self._path: list[str] = path or []
30-
self.__sub_controller_tree: dict[str, BaseController] = {}
30+
self.__sub_controller_tree: dict[str, SubController] = {}
3131

3232
self._bind_attrs()
3333

@@ -47,6 +47,9 @@ def _bind_attrs(self) -> None:
4747
class_type_hints = get_type_hints(type(self))
4848

4949
for attr_name in {**class_dir, **class_type_hints}:
50+
if attr_name == "root_attribute":
51+
continue
52+
5053
attr = getattr(self, attr_name, None)
5154
if isinstance(attr, Attribute):
5255
if (
@@ -59,7 +62,6 @@ def _bind_attrs(self) -> None:
5962
)
6063
new_attribute = copy(attr)
6164
setattr(self, attr_name, new_attribute)
62-
6365
self.attributes[attr_name] = new_attribute
6466

6567
def register_sub_controller(self, name: str, sub_controller: SubController):
@@ -71,7 +73,16 @@ def register_sub_controller(self, name: str, sub_controller: SubController):
7173
self.__sub_controller_tree[name] = sub_controller
7274
sub_controller.set_path(self.path + [name])
7375

74-
def get_sub_controllers(self) -> dict[str, BaseController]:
76+
if isinstance(sub_controller.root_attribute, Attribute):
77+
if name in self.attributes:
78+
raise TypeError(
79+
f"Cannot set SubController `{name}` root attribute "
80+
f"on the parent controller `{type(self).__name__}` "
81+
f"as it already has an attribute of that name."
82+
)
83+
self.attributes[name] = sub_controller.root_attribute
84+
85+
def get_sub_controllers(self) -> dict[str, SubController]:
7586
return self.__sub_controller_tree
7687

7788
def get_controller_mappings(self) -> list[SingleMapping]:
@@ -103,6 +114,7 @@ def _get_single_mapping(controller: BaseController) -> SingleMapping:
103114
for name, attribute in controller.attributes.items()
104115
if attribute.enabled
105116
}
117+
106118
return SingleMapping(
107119
controller, scan_methods, put_methods, command_methods, enabled_attributes
108120
)
@@ -134,5 +146,7 @@ class SubController(BaseController):
134146
it as part of a larger device.
135147
"""
136148

149+
root_attribute: Attribute | None = None
150+
137151
def __init__(self) -> None:
138152
super().__init__()

tests/test_controller.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,36 @@ def test_attribute_parsing():
9191
assert sub_controller_mapping.attributes == {
9292
"sub_attribute": sub_controller.sub_attribute,
9393
}
94+
95+
96+
def test_attribute_in_both_class_and_get_attributes():
97+
class FailingController(Controller):
98+
duplicate_attribute = AttrR(Int())
99+
100+
def __init__(self):
101+
self.attributes = {"duplicate_attribute": AttrR(Int())}
102+
super().__init__()
103+
104+
with pytest.raises(
105+
ValueError,
106+
match=(
107+
"`FailingController` has conflicting attribute `duplicate_attribute` "
108+
"already present in the attributes dict."
109+
),
110+
):
111+
FailingController()
112+
113+
114+
def test_root_attribute():
115+
class FailingController(SomeController):
116+
sub_controller = AttrR(Int())
117+
118+
with pytest.raises(
119+
TypeError,
120+
match=(
121+
"Cannot set SubController `sub_controller` root attribute "
122+
"on the parent controller `FailingController` as it already "
123+
"has an attribute of that name."
124+
),
125+
):
126+
next(_walk_mappings(FailingController(SomeSubController())))

0 commit comments

Comments
 (0)