Skip to content

Commit eaa354b

Browse files
authored
Merge pull request #6865 from jenshnielsen/delegate_parameter_validators
Add source validators to delegate parameter validators
2 parents 83d4cd5 + 8154a2d commit eaa354b

File tree

5 files changed

+203
-3
lines changed

5 files changed

+203
-3
lines changed
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``DelegateParameter`` now includes validators of its source Parameter into its validators. This ensures that a ``DelegateParameter``
2+
with a non numeric source parameter is registered correctly in a measurement when the ``DelegateParameter`` it self does not
3+
set a validator.

src/qcodes/parameters/delegate_parameter.py

+17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from collections.abc import Sequence
99
from datetime import datetime
1010

11+
from qcodes.validators.validators import Validator
12+
1113
from .parameter_base import ParamDataType, ParamRawDataType
1214

1315

@@ -314,3 +316,18 @@ def validate(self, value: ParamDataType) -> None:
314316
super().validate(value)
315317
if self.source is not None:
316318
self.source.validate(self._from_value_to_raw_value(value))
319+
320+
@property
321+
def validators(self) -> tuple[Validator, ...]:
322+
"""
323+
Tuple of all validators associated with the parameter. Note that this
324+
includes validators of the source parameter if source parameter is set
325+
and has any validators.
326+
327+
:getter: All validators associated with the parameter.
328+
"""
329+
source_validators: tuple[Validator, ...] = (
330+
self.source.validators if self.source is not None else ()
331+
)
332+
333+
return tuple(self._vals) + source_validators

src/qcodes/parameters/parameter_base.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,10 @@ def vals(self) -> Validator | None:
383383
RuntimeError: If removing the first validator when more than one validator is set.
384384
385385
"""
386+
validators = self.validators
386387

387-
if len(self._vals):
388-
return self._vals[0]
388+
if len(validators):
389+
return validators[0]
389390
else:
390391
return None
391392

tests/dataset/measurement/test_measurement_context_manager.py

+66-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@
2424
from qcodes.dataset.export_config import DataExportType
2525
from qcodes.dataset.measurements import Measurement
2626
from qcodes.dataset.sqlite.connection import atomic_transaction
27-
from qcodes.parameters import ManualParameter, Parameter, expand_setpoints_helper
27+
from qcodes.parameters import (
28+
DelegateParameter,
29+
ManualParameter,
30+
Parameter,
31+
expand_setpoints_helper,
32+
)
2833
from qcodes.station import Station
34+
from qcodes.validators import ComplexNumbers
2935
from tests.common import retry_until_does_not_throw
3036

3137

@@ -201,6 +207,65 @@ def test_register_custom_parameter(DAC) -> None:
201207
)
202208

203209

210+
def test_register_delegate_parameters() -> None:
211+
x_param = Parameter("x", set_cmd=None, get_cmd=None)
212+
213+
complex_param = Parameter(
214+
"complex_param", get_cmd=None, set_cmd=None, vals=ComplexNumbers()
215+
)
216+
delegate_param = DelegateParameter("delegate", source=complex_param)
217+
218+
meas = Measurement()
219+
220+
meas.register_parameter(x_param)
221+
meas.register_parameter(delegate_param, setpoints=(x_param,))
222+
assert len(meas.parameters) == 2
223+
assert meas.parameters["delegate"].type == "complex"
224+
assert meas.parameters["x"].type == "numeric"
225+
226+
227+
def test_register_delegate_parameters_with_late_source() -> None:
228+
x_param = Parameter("x", set_cmd=None, get_cmd=None)
229+
230+
complex_param = Parameter(
231+
"complex_param", get_cmd=None, set_cmd=None, vals=ComplexNumbers()
232+
)
233+
delegate_param = DelegateParameter("delegate", source=None)
234+
235+
meas = Measurement()
236+
237+
meas.register_parameter(x_param)
238+
239+
delegate_param.source = complex_param
240+
241+
meas.register_parameter(delegate_param, setpoints=(x_param,))
242+
assert len(meas.parameters) == 2
243+
assert meas.parameters["delegate"].type == "complex"
244+
assert meas.parameters["x"].type == "numeric"
245+
246+
247+
def test_register_delegate_parameters_with_late_source_chain():
248+
x_param = Parameter("x", set_cmd=None, get_cmd=None)
249+
250+
complex_param = Parameter(
251+
"complex_param", get_cmd=None, set_cmd=None, vals=ComplexNumbers()
252+
)
253+
delegate_inner = DelegateParameter("delegate_inner", source=None)
254+
delegate_outer = DelegateParameter("delegate_outer", source=None)
255+
256+
meas = Measurement()
257+
258+
meas.register_parameter(x_param)
259+
260+
delegate_outer.source = delegate_inner
261+
delegate_inner.source = complex_param
262+
263+
meas.register_parameter(delegate_outer, setpoints=(x_param,))
264+
assert len(meas.parameters) == 2
265+
assert meas.parameters["delegate_outer"].type == "complex"
266+
assert meas.parameters["x"].type == "numeric"
267+
268+
204269
def test_unregister_parameter(DAC, DMM) -> None:
205270
"""
206271
Test the unregistering of parameters.

tests/parameter/test_delegate_parameter.py

+114
Original file line numberDiff line numberDiff line change
@@ -574,18 +574,23 @@ def test_value_validation() -> None:
574574
source_param = Parameter("source", set_cmd=None, get_cmd=None)
575575
delegate_param = DelegateParameter("delegate", source=source_param)
576576

577+
# Test case where source parameter validator is None and delegate parameter validator is
578+
# specified.
577579
delegate_param.vals = vals.Numbers(-10, 10)
578580
source_param.vals = None
579581
delegate_param.validate(1)
580582
with pytest.raises(ValueError):
581583
delegate_param.validate(11)
582584

585+
# Test where delegate parameter validator is None and source parameter validator is
586+
# specified.
583587
delegate_param.vals = None
584588
source_param.vals = vals.Numbers(-5, 5)
585589
delegate_param.validate(1)
586590
with pytest.raises(ValueError):
587591
delegate_param.validate(6)
588592

593+
# Test case where source parameter validator is more restricted than delegate parameter.
589594
delegate_param.vals = vals.Numbers(-10, 10)
590595
source_param.vals = vals.Numbers(-5, 5)
591596
delegate_param.validate(1)
@@ -594,6 +599,115 @@ def test_value_validation() -> None:
594599
with pytest.raises(ValueError):
595600
delegate_param.validate(11)
596601

602+
# Test case that the order of setting validator on source and delegate parameters does not matter.
603+
source_param.vals = vals.Numbers(-5, 5)
604+
delegate_param.vals = vals.Numbers(-10, 10)
605+
delegate_param.validate(1)
606+
with pytest.raises(ValueError):
607+
delegate_param.validate(6)
608+
with pytest.raises(ValueError):
609+
delegate_param.validate(11)
610+
611+
# Test case where delegate parameter validator is more restricted than source parameter.
612+
delegate_param.vals = vals.Numbers(-5, 5)
613+
source_param.vals = vals.Numbers(-10, 10)
614+
delegate_param.validate(1)
615+
with pytest.raises(ValueError):
616+
delegate_param.validate(6)
617+
with pytest.raises(ValueError):
618+
delegate_param.validate(11)
619+
620+
# Test case that the order of setting validator on source and delegate parameters does not matter.
621+
source_param.vals = vals.Numbers(-10, 10)
622+
delegate_param.vals = vals.Numbers(-5, 5)
623+
delegate_param.validate(1)
624+
with pytest.raises(ValueError):
625+
delegate_param.validate(6)
626+
with pytest.raises(ValueError):
627+
delegate_param.validate(11)
628+
629+
630+
def test_validator_delegates_as_expected() -> None:
631+
source_param = Parameter("source", set_cmd=None, get_cmd=None)
632+
delegate_param = DelegateParameter("delegate", source=source_param)
633+
some_validator = vals.Numbers(-10, 10)
634+
source_param.vals = some_validator
635+
delegate_param.vals = None
636+
delegate_param.validate(1)
637+
with pytest.raises(ValueError):
638+
delegate_param.validate(11)
639+
assert delegate_param.validators == (some_validator,)
640+
assert delegate_param.vals == some_validator
641+
642+
643+
def test_validator_delegates_and_source() -> None:
644+
source_param = Parameter("source", set_cmd=None, get_cmd=None)
645+
delegate_param = DelegateParameter("delegate", source=source_param)
646+
some_validator = vals.Numbers(-10, 10)
647+
some_other_validator = vals.Numbers(-5, 5)
648+
source_param.vals = some_validator
649+
delegate_param.vals = some_other_validator
650+
delegate_param.validate(1)
651+
with pytest.raises(ValueError):
652+
delegate_param.validate(6)
653+
assert delegate_param.validators == (some_other_validator, some_validator)
654+
assert delegate_param.vals == some_other_validator
655+
656+
assert delegate_param.source is not None
657+
delegate_param.source.vals = None
658+
659+
assert delegate_param.validators == (some_other_validator,)
660+
assert delegate_param.vals == some_other_validator
661+
662+
663+
def test_validator_delegates_and_source_chain() -> None:
664+
source_param = Parameter("source", set_cmd=None, get_cmd=None)
665+
delegate_inner = DelegateParameter("delegate_inner", source=source_param)
666+
delegate_outer = DelegateParameter("delegate_outer", source=delegate_inner)
667+
source_validator = vals.Numbers(-10, 10)
668+
delegate_inner_validator = vals.Numbers(-7, 7)
669+
delegate_outer_validator = vals.Numbers(-5, 5)
670+
671+
source_param.vals = source_validator
672+
delegate_inner.vals = delegate_inner_validator
673+
delegate_outer.vals = delegate_outer_validator
674+
675+
delegate_outer.validate(1)
676+
with pytest.raises(ValueError):
677+
delegate_outer.validate(6)
678+
679+
delegate_inner.validate(6)
680+
source_param.validate(6)
681+
682+
assert delegate_outer.validators == (
683+
delegate_outer_validator,
684+
delegate_inner_validator,
685+
source_validator,
686+
)
687+
assert delegate_outer.vals == delegate_outer_validator
688+
689+
assert delegate_inner.validators == (
690+
delegate_inner_validator,
691+
source_validator,
692+
)
693+
assert delegate_inner.vals == delegate_inner_validator
694+
695+
assert delegate_outer.source is not None
696+
delegate_outer.source.vals = None
697+
698+
assert delegate_outer.validators == (
699+
delegate_outer_validator,
700+
source_validator,
701+
)
702+
assert delegate_outer.vals == delegate_outer_validator
703+
704+
assert isinstance(delegate_outer.source, DelegateParameter)
705+
assert delegate_outer.source.source is not None
706+
delegate_outer.source.source.vals = None
707+
708+
assert delegate_outer.validators == (delegate_outer_validator,)
709+
assert delegate_outer.vals == delegate_outer_validator
710+
597711

598712
def test_value_validation_with_offset_and_scale() -> None:
599713
source_param = Parameter(

0 commit comments

Comments
 (0)