11from __future__ import annotations
22
3+ from collections .abc import Callable
34from enum import Enum
45from typing import Any , Generic , Protocol , runtime_checkable
56
@@ -26,7 +27,8 @@ async def put(self, controller: Any, attr: AttrW, value: Any) -> None:
2627class Updater (Protocol ):
2728 """Protocol for updating the cached readback value of an ``Attribute``."""
2829
29- update_period : float
30+ # If update period is None then the attribute will not be updated as a task.
31+ update_period : float | None = None
3032
3133 async def update (self , controller : Any , attr : AttrR ) -> None :
3234 pass
@@ -52,6 +54,7 @@ def __init__(
5254 group : str | None = None ,
5355 handler : Any = None ,
5456 allowed_values : list [T ] | None = None ,
57+ description : str | None = None ,
5558 ) -> None :
5659 assert (
5760 datatype .dtype in ATTRIBUTE_TYPES
@@ -61,6 +64,11 @@ def __init__(
6164 self ._group = group
6265 self .enabled = True
6366 self ._allowed_values : list [T ] | None = allowed_values
67+ self .description = description
68+
69+ # A callback to use when setting the datatype to a different value, for example
70+ # changing the units on an int. This should be implemented in the backend.
71+ self ._update_datatype_callbacks : list [Callable [[DataType [T ]], None ]] = []
6472
6573 @property
6674 def datatype (self ) -> DataType [T ]:
@@ -82,6 +90,20 @@ def group(self) -> str | None:
8290 def allowed_values (self ) -> list [T ] | None :
8391 return self ._allowed_values
8492
93+ def add_update_datatype_callback (
94+ self , callback : Callable [[DataType [T ]], None ]
95+ ) -> None :
96+ self ._update_datatype_callbacks .append (callback )
97+
98+ def update_datatype (self , datatype : DataType [T ]) -> None :
99+ if not isinstance (self ._datatype , type (datatype )):
100+ raise ValueError (
101+ f"Attribute datatype must be of type { type (self ._datatype )} "
102+ )
103+ self ._datatype = datatype
104+ for callback in self ._update_datatype_callbacks :
105+ callback (datatype )
106+
85107
86108class AttrR (Attribute [T ]):
87109 """A read-only ``Attribute``."""
@@ -92,24 +114,29 @@ def __init__(
92114 access_mode = AttrMode .READ ,
93115 group : str | None = None ,
94116 handler : Updater | None = None ,
117+ initial_value : T | None = None ,
95118 allowed_values : list [T ] | None = None ,
119+ description : str | None = None ,
96120 ) -> None :
97121 super ().__init__ (
98122 datatype , # type: ignore
99123 access_mode ,
100124 group ,
101125 handler ,
102126 allowed_values = allowed_values , # type: ignore
127+ description = description ,
128+ )
129+ self ._value : T = (
130+ datatype .initial_value if initial_value is None else initial_value
103131 )
104- self ._value : T = datatype .dtype ()
105132 self ._update_callback : AttrCallback [T ] | None = None
106133 self ._updater = handler
107134
108135 def get (self ) -> T :
109136 return self ._value
110137
111138 async def set (self , value : T ) -> None :
112- self ._value = self ._datatype .dtype (value )
139+ self ._value = self ._datatype .validate (value )
113140
114141 if self ._update_callback is not None :
115142 await self ._update_callback (self ._value )
@@ -132,13 +159,15 @@ def __init__(
132159 group : str | None = None ,
133160 handler : Sender | None = None ,
134161 allowed_values : list [T ] | None = None ,
162+ description : str | None = None ,
135163 ) -> None :
136164 super ().__init__ (
137165 datatype , # type: ignore
138166 access_mode ,
139167 group ,
140168 handler ,
141169 allowed_values = allowed_values , # type: ignore
170+ description = description ,
142171 )
143172 self ._process_callback : AttrCallback [T ] | None = None
144173 self ._write_display_callback : AttrCallback [T ] | None = None
@@ -150,11 +179,11 @@ async def process(self, value: T) -> None:
150179
151180 async def process_without_display_update (self , value : T ) -> None :
152181 if self ._process_callback is not None :
153- await self ._process_callback (self ._datatype .dtype (value ))
182+ await self ._process_callback (self ._datatype .validate (value ))
154183
155184 async def update_display_without_process (self , value : T ) -> None :
156185 if self ._write_display_callback is not None :
157- await self ._write_display_callback (self ._datatype .dtype (value ))
186+ await self ._write_display_callback (self ._datatype .validate (value ))
158187
159188 def set_process_callback (self , callback : AttrCallback [T ] | None ) -> None :
160189 self ._process_callback = callback
@@ -170,7 +199,7 @@ def sender(self) -> Sender | None:
170199 return self ._sender
171200
172201
173- class AttrRW (AttrW [T ], AttrR [T ]):
202+ class AttrRW (AttrR [T ], AttrW [T ]):
174203 """A read-write ``Attribute``."""
175204
176205 def __init__ (
@@ -179,14 +208,18 @@ def __init__(
179208 access_mode = AttrMode .READ_WRITE ,
180209 group : str | None = None ,
181210 handler : Handler | None = None ,
211+ initial_value : T | None = None ,
182212 allowed_values : list [T ] | None = None ,
213+ description : str | None = None ,
183214 ) -> None :
184215 super ().__init__ (
185216 datatype , # type: ignore
186217 access_mode ,
187- group ,
188- handler ,
189- allowed_values , # type: ignore
218+ group = group ,
219+ handler = handler ,
220+ initial_value = initial_value ,
221+ allowed_values = allowed_values , # type: ignore
222+ description = description ,
190223 )
191224
192225 async def process (self , value : T ) -> None :
0 commit comments