77
88from fastcs .attributes import AnyAttributeIO , Attribute , AttrR , AttrW , HintedAttribute
99from fastcs .logging import bind_logger
10+ from fastcs .methods import Command , Scan , UnboundCommand , UnboundScan
1011from fastcs .tracer import Tracer
1112
1213logger = bind_logger (logger_name = __name__ )
@@ -46,6 +47,8 @@ def __init__(
4647 # Internal state that should not be accessed directly by base classes
4748 self .__attributes : dict [str , Attribute ] = {}
4849 self .__sub_controllers : dict [str , BaseController ] = {}
50+ self .__command_methods : dict [str , Command ] = {}
51+ self .__scan_methods : dict [str , Scan ] = {}
4952
5053 self .__hinted_attributes : dict [str , HintedAttribute ] = {}
5154 self .__hinted_sub_controllers : dict [str , type [BaseController ]] = {}
@@ -95,10 +98,6 @@ class method and a controller instance, so that it can be called from any
9598 context with the controller instance passed as the ``self`` argument.
9699
97100 """
98- # Lazy import to avoid circular references
99- from fastcs .methods .command import UnboundCommand
100- from fastcs .methods .scan import UnboundScan
101-
102101 # Using a dictionary instead of a set to maintain order.
103102 class_dir = {key : None for key in dir (type (self )) if not key .startswith ("_" )}
104103 class_type_hints = {
@@ -114,8 +113,21 @@ class method and a controller instance, so that it can be called from any
114113 attr = getattr (self , attr_name , None )
115114 if isinstance (attr , Attribute ):
116115 setattr (self , attr_name , deepcopy (attr ))
117- elif isinstance (attr , UnboundScan | UnboundCommand ):
118- setattr (self , attr_name , attr .bind (self ))
116+ else :
117+ if isinstance (attr , Command ):
118+ self .add_command (attr_name , attr )
119+ elif isinstance (attr , Scan ):
120+ self .add_scan (attr_name , attr )
121+ elif isinstance (
122+ unbound_command := getattr (attr , "__unbound_command__" , None ),
123+ UnboundCommand ,
124+ ):
125+ self .add_command (attr_name , unbound_command .bind (self ))
126+ elif isinstance (
127+ unbound_scan := getattr (attr , "__unbound_scan__" , None ),
128+ UnboundScan ,
129+ ):
130+ self .add_scan (attr_name , unbound_scan .bind (self ))
119131
120132 def _validate_io (self , ios : Sequence [AnyAttributeIO ]):
121133 """Validate that there is exactly one AttributeIO class registered to the
@@ -137,6 +149,10 @@ def __repr__(self):
137149 def __setattr__ (self , name , value ):
138150 if isinstance (value , Attribute ):
139151 self .add_attribute (name , value )
152+ elif isinstance (value , Command ):
153+ self .add_command (name , value )
154+ elif isinstance (value , Scan ):
155+ self .add_scan (name , value )
140156 elif isinstance (value , BaseController ):
141157 self .add_sub_controller (name , value )
142158 else :
@@ -300,3 +316,19 @@ def add_sub_controller(self, name: str, sub_controller: BaseController):
300316 @property
301317 def sub_controllers (self ) -> dict [str , BaseController ]:
302318 return self .__sub_controllers
319+
320+ def add_command (self , name : str , command : Command ):
321+ self .__command_methods [name ] = command
322+ super ().__setattr__ (name , command )
323+
324+ @property
325+ def command_methods (self ) -> dict [str , Command ]:
326+ return self .__command_methods
327+
328+ def add_scan (self , name : str , scan : Scan ):
329+ self .__scan_methods [name ] = scan
330+ super ().__setattr__ (name , scan )
331+
332+ @property
333+ def scan_methods (self ) -> dict [str , Scan ]:
334+ return self .__scan_methods
0 commit comments