Skip to content
Merged
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
24 changes: 20 additions & 4 deletions src/fastcs/controllers/base_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,35 @@ def _find_type_hints(self):
elif isinstance(hint, type) and issubclass(hint, Method):
self.__hinted_methods[name] = hint

@classmethod
def _walk_mro(cls):
"""Return ordered attribute names from the class MRO.

Traverses MRO from base to subclass, collecting attribute names in definition
order while skipping duplicates (subclass overrides take precedence) and private
attributes.
"""
class_dir = []
seen = set()
for base in reversed(cls.__mro__):
for key in base.__dict__:
if not key.startswith("_") and key not in seen:
seen.add(key)
class_dir.append(key)
return class_dir

def _bind_attrs(self) -> None:
"""Search for Attributes and Methods to bind them to this instance.
"""Bind Attributes and Methods to this instance.

This method will search the attributes of this controller class to bind them to
This method will bind the attributes of this controller class to
this specific instance. For Attributes, this is just a case of copying and
re-assigning to ``self`` to make it unique across multiple instances of this
controller class. For Methods, this requires creating a bound method from a
class method and a controller instance, so that it can be called from any
context with the controller instance passed as the ``self`` argument.

"""
# Using a dictionary instead of a set to maintain order.
class_dir = {key: None for key in dir(type(self)) if not key.startswith("_")}
class_dir = dict.fromkeys(self._walk_mro())
class_type_hints = {
key: value
for key, value in get_type_hints(type(self)).items()
Expand Down
24 changes: 14 additions & 10 deletions tests/transports/epics/ca/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,17 @@ def test_get_components(controller_api):
)
],
),
SignalR(name="ReadBool", read_pv="DEVICE:ReadBool", read_widget=LED()),
SignalR(
name="ReadInt",
read_pv="DEVICE:ReadInt",
read_widget=TextRead(),
),
SignalRW(
name="ReadString",
read_pv="DEVICE:ReadString_RBV",
write_pv="DEVICE:ReadString",
name="ReadWriteInt",
write_pv="DEVICE:ReadWriteInt",
write_widget=TextWrite(),
read_pv="DEVICE:ReadWriteInt_RBV",
read_widget=TextRead(),
),
SignalRW(
name="ReadWriteFloat",
Expand All @@ -143,18 +144,21 @@ def test_get_components(controller_api):
read_pv="DEVICE:ReadWriteFloat_RBV",
read_widget=TextRead(),
),
SignalRW(
name="ReadWriteInt",
write_pv="DEVICE:ReadWriteInt",
write_widget=TextWrite(),
read_pv="DEVICE:ReadWriteInt_RBV",
read_widget=TextRead(),
SignalR(
name="ReadBool",
read_pv="DEVICE:ReadBool",
read_widget=LED(),
),
SignalW(
name="WriteBool",
write_pv="DEVICE:WriteBool",
write_widget=ToggleButton(),
),
SignalRW(
name="ReadString",
read_pv="DEVICE:ReadString_RBV",
write_pv="DEVICE:ReadString",
),
SignalX(
name="Go",
write_pv="DEVICE:Go",
Expand Down
12 changes: 6 additions & 6 deletions tests/transports/tango/test_dsr.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ def tango_context(

def test_list_attributes(self, tango_context: DeviceProxy):
assert list(tango_context.get_attribute_list()) == [
"Enum",
"OneDWaveform",
"ReadBool",
"ReadInt",
"ReadString",
"ReadWriteFloat",
"ReadWriteInt",
"TwoDWaveform",
"ReadWriteFloat",
"ReadBool",
"WriteBool",
"ReadString",
"Enum",
"OneDWaveform",
"TwoDWaveform",
"SubController01_ReadInt",
"SubController02_ReadInt",
"State",
Expand Down
Loading