diff --git a/docs/changes/newsfragments/6796.improved b/docs/changes/newsfragments/6796.improved new file mode 100644 index 000000000000..7a7ad7ee0ee9 --- /dev/null +++ b/docs/changes/newsfragments/6796.improved @@ -0,0 +1 @@ +Add vals property and @vals.setter to qcodes.parameters.delegate_parameter.DelegateParameter to propagate the Validator from source to DelegateParameter. diff --git a/src/qcodes/parameters/delegate_parameter.py b/src/qcodes/parameters/delegate_parameter.py index b4c6ecfedf68..46bb7333b36a 100644 --- a/src/qcodes/parameters/delegate_parameter.py +++ b/src/qcodes/parameters/delegate_parameter.py @@ -8,6 +8,8 @@ from collections.abc import Sequence from datetime import datetime + from qcodes.validators import Validator + from .parameter_base import ParamDataType, ParamRawDataType @@ -171,9 +173,11 @@ def __init__( initial_cache_value = kwargs.pop("initial_cache_value", None) self.source = source + self._vals_override: Validator | None = None super().__init__(name, *args, **kwargs) self.label = kwargs.get("label", None) self.unit = kwargs.get("unit", None) + self.vals = kwargs.get("vals", None) # Hack While we inherit the settable status from the parent parameter # we do allow param.set_to to temporary override _settable in a @@ -223,6 +227,23 @@ def unit(self) -> str: def unit(self, unit: str | None) -> None: self._unit_override = unit + @property + def vals(self) -> Validator | None: + """ + The validator of the parameter. Read from source if not explicitly overwritten. + Set to None to disable overwrite. + """ + if self._vals_override is not None: + return self._vals_override + elif self.source is not None: + return self.source.vals + else: + return None + + @vals.setter + def vals(self, vals: Validator | None) -> None: + self._vals_override = vals + @property def label(self) -> str: """ @@ -314,3 +335,5 @@ def validate(self, value: ParamDataType) -> None: super().validate(value) if self.source is not None: self.source.validate(self._from_value_to_raw_value(value)) + if self.vals is not None: + self.vals.validate(value) diff --git a/tests/parameter/test_delegate_parameter.py b/tests/parameter/test_delegate_parameter.py index 07b7d093c0f2..4e8bdb2580c5 100644 --- a/tests/parameter/test_delegate_parameter.py +++ b/tests/parameter/test_delegate_parameter.py @@ -574,18 +574,23 @@ def test_value_validation() -> None: source_param = Parameter("source", set_cmd=None, get_cmd=None) delegate_param = DelegateParameter("delegate", source=source_param) + # Test case where source parameter validator is None and delegate parameter validator is + # specified. delegate_param.vals = vals.Numbers(-10, 10) source_param.vals = None delegate_param.validate(1) with pytest.raises(ValueError): delegate_param.validate(11) + # Test where delegate parameter validator is None and source parameter validator is + # specified. delegate_param.vals = None source_param.vals = vals.Numbers(-5, 5) delegate_param.validate(1) with pytest.raises(ValueError): delegate_param.validate(6) + # Test case where source parameter validator is more restricted than delegate parameter. delegate_param.vals = vals.Numbers(-10, 10) source_param.vals = vals.Numbers(-5, 5) delegate_param.validate(1) @@ -594,6 +599,33 @@ def test_value_validation() -> None: with pytest.raises(ValueError): delegate_param.validate(11) + # Test case that the order of setting validator on source and delegate parameters does not matter. + source_param.vals = vals.Numbers(-5, 5) + delegate_param.vals = vals.Numbers(-10, 10) + delegate_param.validate(1) + with pytest.raises(ValueError): + delegate_param.validate(6) + with pytest.raises(ValueError): + delegate_param.validate(11) + + # Test case where delegate parameter validator is more restricted than source parameter. + delegate_param.vals = vals.Numbers(-5, 5) + source_param.vals = vals.Numbers(-10, 10) + delegate_param.validate(1) + with pytest.raises(ValueError): + delegate_param.validate(6) + with pytest.raises(ValueError): + delegate_param.validate(11) + + # Test case that the order of setting validator on source and delegate parameters does not matter. + source_param.vals = vals.Numbers(-10, 10) + delegate_param.vals = vals.Numbers(-5, 5) + delegate_param.validate(1) + with pytest.raises(ValueError): + delegate_param.validate(6) + with pytest.raises(ValueError): + delegate_param.validate(11) + def test_value_validation_with_offset_and_scale() -> None: source_param = Parameter(