Skip to content

Commit

Permalink
Merge pull request #5426 from jenshnielsen/multi_validator
Browse files Browse the repository at this point in the history
Allow additional validators on parameter at runtime
  • Loading branch information
jenshnielsen committed Oct 11, 2023
2 parents 039a1f8 + e61314d commit bf76977
Show file tree
Hide file tree
Showing 5 changed files with 406 additions and 36 deletions.
2 changes: 2 additions & 0 deletions docs/changes/newsfragments/5426.new
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The QCoDeS Parameters have nov gained the ability to use multiple validators.
See `here <../examples/Parameters/Parameters.ipynb>`__ for examples of how to use this.
110 changes: 93 additions & 17 deletions docs/examples/Parameters/Parameters.ipynb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -50,10 +57,10 @@
"source": [
"from typing import Optional\n",
"\n",
"from qcodes.instrument.base import InstrumentBase\n",
"from qcodes import validators\n",
"from qcodes.instrument import InstrumentBase\n",
"from qcodes.parameters import Parameter\n",
"from qcodes.tests.instrument_mocks import DummyInstrument\n",
"from qcodes.utils import validators"
"from qcodes.tests.instrument_mocks import DummyInstrument"
]
},
{
Expand Down Expand Up @@ -88,12 +95,12 @@
" docstring='counts how many times get has been called '\n",
" 'but can be reset to any integer >= 0 by set')\n",
" self._count = 0\n",
" \n",
"\n",
" # you must provide a get method, a set method, or both.\n",
" def get_raw(self):\n",
" self._count += 1\n",
" return self._count\n",
" \n",
"\n",
" def set_raw(self, val):\n",
" self._count = val\n",
" return self._count"
Expand Down Expand Up @@ -201,11 +208,11 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"from qcodes.instrument import DelegateParameter"
"from qcodes.parameters import DelegateParameter"
]
},
{
Expand All @@ -217,7 +224,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 8,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -248,7 +255,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -257,7 +264,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 10,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -297,7 +304,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 11,
"metadata": {
"ExecuteTime": {
"end_time": "2021-06-17T08:58:52.491912Z",
Expand All @@ -310,13 +317,13 @@
" def __init__(self, name, dac_param):\n",
" self._dac_param = dac_param\n",
" super().__init__(name)\n",
" \n",
"\n",
" @property\n",
" def underlying_instrument(self) -> Optional[InstrumentBase]:\n",
" return self._dac_param.root_instrument\n",
" \n",
"\n",
" def get_raw(self):\n",
" return self._dac_param.get() "
" return self._dac_param.get()"
]
},
{
Expand All @@ -330,7 +337,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 12,
"metadata": {
"ExecuteTime": {
"end_time": "2021-06-17T08:58:48.861043Z",
Expand All @@ -351,7 +358,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 13,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -382,6 +389,75 @@
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Validating parameters"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When a parameter is set it is automatically validated against any validator set for that parameter. Most QCoDeS instrument drivers are configured to only validate input that the instrument can actually set. In the example of dac.ch1 the values that can be set are between -800 and 400. Sometimes it may however, be useful to be able to restrict the valid values even further, perhaps due to limitations in the range of a device under test or a specific calibration range. This can be done either by adding an additional validator or by using the `extra_validator` context manager. "
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Default validators: (<Numbers -800<=v<=400>,)\n",
"Failed to validate setting to 450.\n",
"Validator added: (<Numbers -800<=v<=400>, <Numbers -500<=v<=300>)\n",
"Failed to validate setting to 350.\n",
"Validator context: (<Numbers -800<=v<=400>, <Numbers -500<=v<=-100>)\n",
"Failed to validate setting to 250 within context manager.\n"
]
}
],
"source": [
"dac.ch1\n",
"print(f\"Default validators: {dac.ch1.validators}\")\n",
"\n",
"# if we try to set a value outside the range of the default validator we get an error.\n",
"try:\n",
" dac.ch1(450)\n",
"except Exception:\n",
" print(\"Failed to validate setting to 450.\")\n",
"\n",
"\n",
"dac.ch1.add_validator(validators.Numbers(-500,300))\n",
"print(f\"Validator added: {dac.ch1.validators}\")\n",
"\n",
"# with the new validator set we cannot set the value outside its range.\n",
"try:\n",
" dac.ch1(350)\n",
"except Exception:\n",
" print(\"Failed to validate setting to 350.\")\n",
"\n",
"# but once it is removed we can again set any value in the range of the original validator.\n",
"dac.ch1.remove_validator()\n",
"dac.ch1(350)\n",
"\n",
"# we can also temorarily add a validator using a context manager.\n",
"\n",
"with dac.ch1.extra_validator(validators.Numbers(-500,-100)):\n",
" print(f\"Validator context: {dac.ch1.validators}\")\n",
" # with the new validator set we cannot set the value outside its range.\n",
" try:\n",
" dac.ch1(250)\n",
" except Exception:\n",
" print(\"Failed to validate setting to 250 within context manager.\")\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -406,7 +482,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
"version": "3.11.5"
},
"toc": {
"base_numbering": 1,
Expand Down
25 changes: 18 additions & 7 deletions qcodes/parameters/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,20 +329,31 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType:
if initial_cache_value is not None:
self.cache.set(initial_cache_value)

self._docstring = docstring
self.__doc__ = self._build__doc__()

def _build__doc__(self) -> str:
if len(self.validators) == 0:
validator_docstrings = ["* `vals` None"]
else:
validator_docstrings = [
f"* `vals` {validator!r}" for validator in self.validators
]
# generate default docstring
self.__doc__ = os.linesep.join(
doc = os.linesep.join(
(
"Parameter class:",
"",
"* `name` %s" % self.name,
"* `label` %s" % self.label,
"* `unit` %s" % self.unit,
"* `vals` %s" % repr(self.vals),
f"* `name` {self.name}",
f"* `label` {self.label}",
f"* `unit` {self.unit}",
*validator_docstrings,
)
)
if self._docstring is not None:
doc = os.linesep.join((self._docstring, "", doc))

if docstring is not None:
self.__doc__ = os.linesep.join((docstring, "", self.__doc__))
return doc

@property
def unit(self) -> str:
Expand Down

0 comments on commit bf76977

Please sign in to comment.