Skip to content

Commit ecdc15f

Browse files
author
James Souter
committed
rework test_dynamic_attribute_io_specification
1 parent f54bd67 commit ecdc15f

File tree

1 file changed

+89
-55
lines changed

1 file changed

+89
-55
lines changed

tests/test_attribute.py

Lines changed: 89 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -93,85 +93,123 @@ class ControllerNoIO(Controller):
9393
await c.my_attr.update()
9494

9595

96+
class DummyConnection:
97+
def __init__(self):
98+
self._connected = False
99+
self._int_value = 5
100+
self._ro_int_value = 10
101+
self._float_value = 7.5
102+
103+
async def connect(self):
104+
self._connected = True
105+
106+
async def get(self, uri: str):
107+
if not self._connected:
108+
raise TimeoutError("No response from DummyConnection")
109+
if uri == "config/introspect_api":
110+
return [
111+
{
112+
"name": "int_parameter",
113+
"subsystem": "status",
114+
"dtype": "int",
115+
"min": 0,
116+
"max": 100,
117+
"value": self._int_value,
118+
"read_only": False,
119+
},
120+
{
121+
"name": "ro_int_parameter",
122+
"subsystem": "status",
123+
"dtype": "int",
124+
"value": self._ro_int_value,
125+
"read_only": True,
126+
},
127+
{
128+
"name": "float_parameter",
129+
"subsystem": "status",
130+
"dtype": "float",
131+
"max": 1000.0,
132+
"value": self._float_value,
133+
"read_only": False,
134+
},
135+
]
136+
137+
# increment after getting
138+
elif uri == "status/int_parameter":
139+
value = self._int_value
140+
self._int_value += 1
141+
elif uri == "status/ro_int_parameter":
142+
value = self._ro_int_value
143+
self._ro_int_value += 1
144+
elif uri == "status/float_parameter":
145+
value = self._float_value
146+
self._float_value += 1
147+
return value
148+
149+
async def set(self, uri: str, value: float | int):
150+
if uri == "status/int_parameter":
151+
self._int_value = value
152+
elif uri == "status/ro_int_parameter":
153+
# don't update read only parameter
154+
pass
155+
elif uri == "status/float_parameter":
156+
self._float_value = value
157+
158+
96159
@pytest.mark.asyncio()
97160
async def test_dynamic_attribute_io_specification():
98-
example_introspection_response = [
99-
{
100-
"name": "int_parameter",
101-
"dtype": "int",
102-
"min": 0,
103-
"max": 100,
104-
"value": 5,
105-
"read_only": False,
106-
},
107-
{"name": "ro_int_parameter", "dtype": "int", "value": 10, "read_only": True},
108-
{
109-
"name": "float_parameter",
110-
"dtype": "float",
111-
"max": 1000.0,
112-
"value": 7.5,
113-
"read_only": False,
114-
},
115-
]
116-
117161
@dataclass
118162
class DemoParameterAttributeIORef(AttributeIORef, Generic[NumberT]):
119163
name: str
120-
# TODO, this is weird, we should just use the attributes's min and max fields
121-
min: NumberT | None = None
122-
max: NumberT | None = None
123-
read_only: bool = False
164+
subsystem: str
165+
connection: DummyConnection
166+
167+
@property
168+
def uri(self):
169+
return f"{self.subsystem}/{self.name}"
124170

125171
class DemoParameterAttributeIO(AttributeIO[NumberT, DemoParameterAttributeIORef]):
126172
async def update(
127173
self,
128174
attr: AttrR[NumberT, DemoParameterAttributeIORef],
129175
):
130-
# OK, so this doesn't really work when we have min and maxes...
131-
await attr.set(attr.get() + 1)
176+
value = await attr.io_ref.connection.get(attr.io_ref.uri)
177+
await attr.set(value)
132178

133179
async def send(
134180
self,
135181
attr: AttrW[NumberT, DemoParameterAttributeIORef],
136182
value: NumberT,
137183
) -> None:
138-
if (
139-
attr.io_ref.read_only
140-
): # TODO, this isn't necessary as we can not call process on this anyway
141-
raise RuntimeError(
142-
f"Could not set read only attribute {attr.io_ref.name}"
143-
)
144-
145-
if (io_min := attr.io_ref.min) is not None and value < io_min:
146-
raise RuntimeError(
147-
f"Could not set {attr.io_ref.name} to {value}, min is {io_min}"
148-
)
149-
150-
if (io_max := attr.io_ref.max) is not None and value > io_max:
151-
raise RuntimeError(
152-
f"Could not set {attr.io_ref.name} to {value}, max is {io_max}"
153-
)
154-
# TODO: we should always end send with a update_display_without_process...
184+
await attr.io_ref.connection.set(attr.io_ref.uri, value)
185+
await self.update(attr)
155186

156187
class DemoParameterController(Controller):
157188
ro_int_parameter: AttrR
158189
int_parameter: AttrRW
159190
float_parameter: AttrRW # hint to satisfy pyright
160191

161192
async def initialise(self):
162-
dtype_mapping = {"int": Int(), "float": Float()}
193+
self._connection = DummyConnection()
194+
await self._connection.connect()
195+
dtype_mapping = {"int": Int, "float": Float}
196+
example_introspection_response = await self._connection.get(
197+
"config/introspect_api"
198+
)
163199
for parameter_response in example_introspection_response:
164200
try:
165201
ro = parameter_response["read_only"]
166202
ref = DemoParameterAttributeIORef(
167203
name=parameter_response["name"],
168-
min=parameter_response.get("min", None),
169-
max=parameter_response.get("max", None),
170-
read_only=ro,
204+
subsystem=parameter_response["subsystem"],
205+
connection=self._connection,
171206
)
172207
attr_class = AttrR if ro else AttrRW
173208
attr = attr_class(
174-
datatype=dtype_mapping[parameter_response["dtype"]],
209+
datatype=dtype_mapping[parameter_response["dtype"]](
210+
min=parameter_response.get("min", None),
211+
max=parameter_response.get("max", None),
212+
),
175213
io_ref=ref,
176214
initial_value=parameter_response.get("value", None),
177215
)
@@ -190,16 +228,12 @@ async def initialise(self):
190228
await c.initialise()
191229
await c.attribute_initialise()
192230
await c.ro_int_parameter.update()
231+
assert c.ro_int_parameter.get() == 10
232+
await c.ro_int_parameter.update()
193233
assert c.ro_int_parameter.get() == 11
194-
with pytest.raises(
195-
RuntimeError, match="Could not set int_parameter to -10, min is 0"
196-
):
197-
await c.int_parameter.process(-10)
198234

199-
with pytest.raises(
200-
RuntimeError, match="Could not set int_parameter to 101, max is 100"
201-
):
202-
await c.int_parameter.process(101)
235+
await c.int_parameter.process(20)
236+
assert c.int_parameter.get() == 20
203237

204238

205239
@pytest.mark.asyncio

0 commit comments

Comments
 (0)