From ef3c78e4e84ecc8372d5837fdd47d23dd88e486c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 4 Feb 2024 08:00:43 +0100 Subject: [PATCH 01/15] convert Command call into a method to match get/set_raw on other parameters --- src/qcodes/parameters/parameter.py | 35 +++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 29565201fe93..c741224eb32f 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -5,6 +5,7 @@ import logging import os +from functools import wraps from types import MethodType from typing import TYPE_CHECKING, Any, Literal @@ -213,6 +214,20 @@ def _set_manual_parameter( self.cache._set_from_raw_value(x) return x + def _get_command_caller(parameter: Parameter, command: Command) -> MethodType: + @wraps(Command.__call__) + def call_command(self: Parameter) -> Any: + return command() + + return MethodType(call_command, parameter) + + def _set_command_caller(parameter: Parameter, command: Command) -> MethodType: + @wraps(Command.__call__) + def call_command(self: Parameter, val: ParamRawDataType) -> Any: + return command(val) + + return MethodType(call_command, parameter) + if instrument is not None and bind_to_instrument: existing_parameter = instrument.parameters.get(name, None) @@ -280,13 +295,14 @@ def _set_manual_parameter( ) exec_str_ask = getattr(instrument, "ask", None) if instrument else None - # TODO get_raw should also be a method here. This should probably be done by wrapping - # it with MethodType like above # ignore typeerror since mypy does not allow setting a method dynamically - self.get_raw = Command( # type: ignore[method-assign] - arg_count=0, - cmd=get_cmd, - exec_str=exec_str_ask, + self.get_raw = _get_command_caller( # type: ignore[method-assign] + self, + Command( + arg_count=0, + cmd=get_cmd, + exec_str=exec_str_ask, + ), ) self._gettable = True # mypy resolves the type of self.get_raw to object here. @@ -314,11 +330,10 @@ def _set_manual_parameter( exec_str_write = ( getattr(instrument, "write", None) if instrument else None ) - # TODO get_raw should also be a method here. This should probably be done by wrapping - # it with MethodType like above # ignore typeerror since mypy does not allow setting a method dynamically - self.set_raw = Command( # type: ignore[assignment] - arg_count=1, cmd=set_cmd, exec_str=exec_str_write + self.set_raw = _set_command_caller( # type: ignore[method-assign] + self, + Command(arg_count=1, cmd=set_cmd, exec_str=exec_str_write), ) self._settable = True self.set = self._wrap_set(self.set_raw) From 681cbfa333cd320e435c784c371779dddd976056 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 4 Feb 2024 09:21:24 +0100 Subject: [PATCH 02/15] better error message in command --- src/qcodes/parameters/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qcodes/parameters/command.py b/src/qcodes/parameters/command.py index 18815f6beb41..81459430aa49 100644 --- a/src/qcodes/parameters/command.py +++ b/src/qcodes/parameters/command.py @@ -144,7 +144,7 @@ def __init__( else: raise TypeError( - f"cmd must be a string or function with arg_count={arg_count} args" + f"cmd must be a string or function with arg_count={arg_count} args not {cmd} of type {type(cmd)}" ) # Wrappers that may or may not be used in constructing call From 7562a2ff8cb17f93827101d7284a78a99987d71b Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 4 Feb 2024 09:44:51 +0100 Subject: [PATCH 03/15] debug reraise error --- src/qcodes/utils/function_helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qcodes/utils/function_helpers.py b/src/qcodes/utils/function_helpers.py index 1fca19de39ed..ba919efcae99 100644 --- a/src/qcodes/utils/function_helpers.py +++ b/src/qcodes/utils/function_helpers.py @@ -40,5 +40,6 @@ def is_function(f: object, arg_count: int, coroutine: bool = False) -> bool: inputs = [0] * arg_count sig.bind(*inputs) return True - except TypeError: + except TypeError as e: + raise e return False From 2fbf9a62160cf5a2f106c65550a30cb2ba5a9e91 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 8 May 2024 13:26:13 +0200 Subject: [PATCH 04/15] Remove unneeded ignores --- src/qcodes/parameters/parameter.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index c741224eb32f..a4912d0d1921 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -305,9 +305,7 @@ def call_command(self: Parameter, val: ParamRawDataType) -> Any: ), ) self._gettable = True - # mypy resolves the type of self.get_raw to object here. - # this may be resolvable if Command above is correctly wrapped in MethodType - self.get = self._wrap_get(self.get_raw) # type: ignore[arg-type] + self.get = self._wrap_get(self.get_raw) if self.settable and set_cmd not in (None, False): raise TypeError( From 4e69416ea03af88eb37db3903485660efc3cca64 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 13 May 2024 21:13:22 +0200 Subject: [PATCH 05/15] Avoid using Command for get --- src/qcodes/parameters/parameter.py | 117 +++++++++++++++++---------- src/qcodes/utils/function_helpers.py | 3 +- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index a4912d0d1921..ea9306da3d85 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -5,16 +5,17 @@ import logging import os -from functools import wraps +from collections.abc import Callable from types import MethodType -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any, Literal, cast + +from qcodes.utils import is_function from .command import Command from .parameter_base import ParamDataType, ParameterBase, ParamRawDataType from .sweep_values import SweepFixedValues if TYPE_CHECKING: - from collections.abc import Callable from qcodes.instrument.base import InstrumentBase from qcodes.logger.instrument_logger import InstrumentLoggerAdapter @@ -24,6 +25,65 @@ log = logging.getLogger(__name__) +def _get_parameter_factory( + function: Callable[[str], ParamRawDataType] | None, + cmd: str | Callable[[], ParamRawDataType] | None, + parameter_name: str, +) -> Callable[[Parameter], ParamRawDataType]: + if cmd is None: + + def get_manual_parameter(self: Parameter) -> ParamRawDataType: + if self.root_instrument is not None: + mylogger: InstrumentLoggerAdapter | logging.Logger = ( + self.root_instrument.log + ) + else: + mylogger = log + mylogger.debug( + "Getting raw value of parameter: %s as %s", + self.full_name, + self.cache.raw_value, + ) + return self.cache.raw_value + + return get_manual_parameter + + elif isinstance(cmd, str) and is_function(function, 1): + # cast is safe since we just checked this above using is_function + function = cast(Callable[[str], ParamRawDataType], function) + + def get_parameter_ask(self: Parameter) -> ParamRawDataType: + # for some reason mypy does not understand + # that cmd is a str even if this is defined inside + # an if isinstance block + assert isinstance(cmd, str) + return function(cmd) + + return get_parameter_ask + + elif is_function(cmd, 0): + # cast is safe since we just checked this above using is_function + cmd = cast(Callable[[], ParamRawDataType], cmd) + + def get_parameter_func(self: Parameter) -> ParamRawDataType: + return cmd() + + return get_parameter_func + + elif isinstance(cmd, str) and function is None: + raise TypeError( + f"Cannot use a str get_cmd without " + f"binding to an instrument. " + f"Got: get_cmd {cmd} for parameter {parameter_name}" + ) + + else: + raise TypeError( + "Unexpected options for parameter get. " + f"Got: get_cmd {cmd} for parameter {parameter_name}" + ) + + class Parameter(ParameterBase): """ A parameter represents a single degree of freedom. Most often, @@ -175,7 +235,7 @@ def __init__( instrument: InstrumentBase | None = None, label: str | None = None, unit: str | None = None, - get_cmd: str | Callable[..., Any] | Literal[False] | None = None, + get_cmd: str | Callable[[], ParamRawDataType] | Literal[False] | None = None, set_cmd: str | Callable[..., Any] | Literal[False] | None = False, initial_value: float | str | None = None, max_val_age: float | None = None, @@ -214,20 +274,6 @@ def _set_manual_parameter( self.cache._set_from_raw_value(x) return x - def _get_command_caller(parameter: Parameter, command: Command) -> MethodType: - @wraps(Command.__call__) - def call_command(self: Parameter) -> Any: - return command() - - return MethodType(call_command, parameter) - - def _set_command_caller(parameter: Parameter, command: Command) -> MethodType: - @wraps(Command.__call__) - def call_command(self: Parameter, val: ParamRawDataType) -> Any: - return command(val) - - return MethodType(call_command, parameter) - if instrument is not None and bind_to_instrument: existing_parameter = instrument.parameters.get(name, None) @@ -283,27 +329,15 @@ def call_command(self: Parameter, val: ParamRawDataType) -> Any: " get_raw is an error." ) elif not self.gettable and get_cmd is not False: - if get_cmd is None: - # ignore typeerror since mypy does not allow setting a method dynamically - self.get_raw = MethodType(_get_manual_parameter, self) # type: ignore[method-assign] - else: - if isinstance(get_cmd, str) and instrument is None: - raise TypeError( - f"Cannot use a str get_cmd without " - f"binding to an instrument. " - f"Got: get_cmd {get_cmd} for parameter {name}" - ) + exec_str_ask: Callable[[str], ParamRawDataType] | None = ( + getattr(instrument, "ask", None) if instrument else None + ) + + self.get_raw = MethodType( # type: ignore[method-assign] + _get_parameter_factory(exec_str_ask, cmd=get_cmd, parameter_name=name), + self, + ) - exec_str_ask = getattr(instrument, "ask", None) if instrument else None - # ignore typeerror since mypy does not allow setting a method dynamically - self.get_raw = _get_command_caller( # type: ignore[method-assign] - self, - Command( - arg_count=0, - cmd=get_cmd, - exec_str=exec_str_ask, - ), - ) self._gettable = True self.get = self._wrap_get(self.get_raw) @@ -328,10 +362,11 @@ def call_command(self: Parameter, val: ParamRawDataType) -> Any: exec_str_write = ( getattr(instrument, "write", None) if instrument else None ) + # TODO get_raw should also be a method here. This should probably be done by wrapping + # it with MethodType like above # ignore typeerror since mypy does not allow setting a method dynamically - self.set_raw = _set_command_caller( # type: ignore[method-assign] - self, - Command(arg_count=1, cmd=set_cmd, exec_str=exec_str_write), + self.set_raw = Command( # type: ignore[assignment] + arg_count=1, cmd=set_cmd, exec_str=exec_str_write ) self._settable = True self.set = self._wrap_set(self.set_raw) diff --git a/src/qcodes/utils/function_helpers.py b/src/qcodes/utils/function_helpers.py index ba919efcae99..1fca19de39ed 100644 --- a/src/qcodes/utils/function_helpers.py +++ b/src/qcodes/utils/function_helpers.py @@ -40,6 +40,5 @@ def is_function(f: object, arg_count: int, coroutine: bool = False) -> bool: inputs = [0] * arg_count sig.bind(*inputs) return True - except TypeError as e: - raise e + except TypeError: return False From 4dc0211c13aa048c1a7d40a75a05d7ae100e73b1 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 07:18:09 +0200 Subject: [PATCH 06/15] format error better --- src/qcodes/parameters/parameter.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index ea9306da3d85..a4a750fdf8fd 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -325,8 +325,7 @@ def _set_manual_parameter( if self.gettable and get_cmd not in (None, False): raise TypeError( "Supplying a not None or False `get_cmd` to a Parameter" - " that already implements" - " get_raw is an error." + " that already implements get_raw is an error." ) elif not self.gettable and get_cmd is not False: exec_str_ask: Callable[[str], ParamRawDataType] | None = ( @@ -344,8 +343,7 @@ def _set_manual_parameter( if self.settable and set_cmd not in (None, False): raise TypeError( "Supplying a not None or False `set_cmd` to a Parameter" - " that already implements" - " set_raw is an error." + " that already implements set_raw is an error." ) elif not self.settable and set_cmd is not False: if set_cmd is None: From a699e269357dd67616ad84a88682fd1a64491ba2 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 07:34:37 +0200 Subject: [PATCH 07/15] Remove no longer uses internal func --- src/qcodes/parameters/parameter.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index a4a750fdf8fd..d6fb09a76633 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -245,20 +245,6 @@ def __init__( bind_to_instrument: bool = True, **kwargs: Any, ) -> None: - def _get_manual_parameter(self: Parameter) -> ParamRawDataType: - if self.root_instrument is not None: - mylogger: InstrumentLoggerAdapter | logging.Logger = ( - self.root_instrument.log - ) - else: - mylogger = log - mylogger.debug( - "Getting raw value of parameter: %s as %s", - self.full_name, - self.cache.raw_value, - ) - return self.cache.raw_value - def _set_manual_parameter( self: Parameter, x: ParamRawDataType ) -> ParamRawDataType: From fdd70abe064f63c3a86160491a011d54bbc94a02 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 07:47:05 +0200 Subject: [PATCH 08/15] Clarify a few things for get --- src/qcodes/parameters/parameter.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index d6fb09a76633..9acaa618e606 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -52,11 +52,14 @@ def get_manual_parameter(self: Parameter) -> ParamRawDataType: # cast is safe since we just checked this above using is_function function = cast(Callable[[str], ParamRawDataType], function) - def get_parameter_ask(self: Parameter) -> ParamRawDataType: + def get_parameter_ask(self: Parameter, *args) -> ParamRawDataType: # for some reason mypy does not understand # that cmd is a str even if this is defined inside # an if isinstance block assert isinstance(cmd, str) + # TODO it is possible to format str with additional args. + # this does not seem to have been tested + cmd.format(*args) return function(cmd) return get_parameter_ask @@ -317,7 +320,7 @@ def _set_manual_parameter( exec_str_ask: Callable[[str], ParamRawDataType] | None = ( getattr(instrument, "ask", None) if instrument else None ) - + # ignore typeerror since mypy does not allow setting a method dynamically self.get_raw = MethodType( # type: ignore[method-assign] _get_parameter_factory(exec_str_ask, cmd=get_cmd, parameter_name=name), self, From 4c56c7ed4fbb1d17f59e519ba77c6c741afd6cdb Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 07:47:38 +0200 Subject: [PATCH 09/15] Replace set_cmd with method --- src/qcodes/parameters/parameter.py | 107 +++++++++++++++++++---------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 9acaa618e606..07101b94c3f3 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -11,7 +11,6 @@ from qcodes.utils import is_function -from .command import Command from .parameter_base import ParamDataType, ParameterBase, ParamRawDataType from .sweep_values import SweepFixedValues @@ -25,6 +24,67 @@ log = logging.getLogger(__name__) +def _set_parameter_factory( + function: Callable[[str], ParamRawDataType] | None, + cmd: str | Callable[[ParamRawDataType], None] | None, + parameter_name: str, +) -> Callable[[Parameter, ParamRawDataType], None]: + if cmd is None: + + def _set_manual_parameter( + self: Parameter, x: ParamRawDataType + ) -> ParamRawDataType: + if self.root_instrument is not None: + mylogger: InstrumentLoggerAdapter | logging.Logger = ( + self.root_instrument.log + ) + else: + mylogger = log + mylogger.debug( + "Setting raw value of parameter: %s to %s", self.full_name, x + ) + self.cache._set_from_raw_value(x) + return x + + return _set_manual_parameter + elif isinstance(cmd, str) and is_function(function, 1): + # cast is safe since we just checked this above using is_function + function = cast(Callable[[str], ParamRawDataType], function) + + def set_parameter_write(self: Parameter, *args) -> None: + # for some reason mypy does not understand + # that cmd is a str even if this is defined inside + # an if isinstance block + assert isinstance(cmd, str) + # TODO it is possible to format str with additional args. + # this does not seem to have been tested + cmd.format(*args) + return function(cmd) + + return set_parameter_write + + elif is_function(cmd, 1): + # cast is safe since we just checked this above using is_function + cmd = cast(Callable[[ParamRawDataType], None], cmd) + + def set_parameter_func(self: Parameter, value: ParamRawDataType) -> None: + return cmd(value) + + return set_parameter_func + + elif isinstance(cmd, str) and function is None: + raise TypeError( + f"Cannot use a str set_cmd without " + f"binding to an instrument. " + f"Got: set_cmd {cmd} for parameter {parameter_name}" + ) + else: + raise TypeError( + "Unexpected options for parameter set. " + f"Got: set_cmd {cmd} for parameter {parameter_name}" + ) + + def _get_parameter_factory( function: Callable[[str], ParamRawDataType] | None, cmd: str | Callable[[], ParamRawDataType] | None, @@ -248,21 +308,6 @@ def __init__( bind_to_instrument: bool = True, **kwargs: Any, ) -> None: - def _set_manual_parameter( - self: Parameter, x: ParamRawDataType - ) -> ParamRawDataType: - if self.root_instrument is not None: - mylogger: InstrumentLoggerAdapter | logging.Logger = ( - self.root_instrument.log - ) - else: - mylogger = log - mylogger.debug( - "Setting raw value of parameter: %s to %s", self.full_name, x - ) - self.cache._set_from_raw_value(x) - return x - if instrument is not None and bind_to_instrument: existing_parameter = instrument.parameters.get(name, None) @@ -335,26 +380,16 @@ def _set_manual_parameter( " that already implements set_raw is an error." ) elif not self.settable and set_cmd is not False: - if set_cmd is None: - # ignore typeerror since mypy does not allow setting a method dynamically - self.set_raw = MethodType(_set_manual_parameter, self) # type: ignore[method-assign] - else: - if isinstance(set_cmd, str) and instrument is None: - raise TypeError( - f"Cannot use a str set_cmd without " - f"binding to an instrument. " - f"Got: set_cmd {set_cmd} for parameter {name}" - ) - - exec_str_write = ( - getattr(instrument, "write", None) if instrument else None - ) - # TODO get_raw should also be a method here. This should probably be done by wrapping - # it with MethodType like above - # ignore typeerror since mypy does not allow setting a method dynamically - self.set_raw = Command( # type: ignore[assignment] - arg_count=1, cmd=set_cmd, exec_str=exec_str_write - ) + exec_str_write: Any | None = ( + getattr(instrument, "write", None) if instrument else None + ) + # ignore typeerror since mypy does not allow setting a method dynamically + self.set_raw = MethodType( # type: ignore[method-assign] + _set_parameter_factory( + exec_str_write, cmd=set_cmd, parameter_name=name + ), + self, + ) self._settable = True self.set = self._wrap_set(self.set_raw) From fc1305c1806394bd065e7b4485080ae629e4877d Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 08:53:44 +0200 Subject: [PATCH 10/15] Fix get formatter --- src/qcodes/parameters/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 07101b94c3f3..5a396c343368 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -119,8 +119,8 @@ def get_parameter_ask(self: Parameter, *args) -> ParamRawDataType: assert isinstance(cmd, str) # TODO it is possible to format str with additional args. # this does not seem to have been tested - cmd.format(*args) - return function(cmd) + formatted_cmd = cmd.format(*args) + return function(formatted_cmd) return get_parameter_ask From beab2c27854222993df8e08f1fc1924564e62955 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 20 May 2024 08:53:59 +0200 Subject: [PATCH 11/15] Fix set formatter --- src/qcodes/parameters/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 5a396c343368..1b9a67c15966 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -58,8 +58,8 @@ def set_parameter_write(self: Parameter, *args) -> None: assert isinstance(cmd, str) # TODO it is possible to format str with additional args. # this does not seem to have been tested - cmd.format(*args) - return function(cmd) + formatted_cmd = cmd.format(*args) + return function(formatted_cmd) return set_parameter_write From 5ee06bc906dc0e1e352afebcfece9dc4418ccdd1 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 9 Jun 2024 18:40:07 +0200 Subject: [PATCH 12/15] Add missing type annotations --- src/qcodes/parameters/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 1b9a67c15966..f58437b19f1d 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -51,7 +51,7 @@ def _set_manual_parameter( # cast is safe since we just checked this above using is_function function = cast(Callable[[str], ParamRawDataType], function) - def set_parameter_write(self: Parameter, *args) -> None: + def set_parameter_write(self: Parameter, *args: Any) -> None: # for some reason mypy does not understand # that cmd is a str even if this is defined inside # an if isinstance block @@ -112,7 +112,7 @@ def get_manual_parameter(self: Parameter) -> ParamRawDataType: # cast is safe since we just checked this above using is_function function = cast(Callable[[str], ParamRawDataType], function) - def get_parameter_ask(self: Parameter, *args) -> ParamRawDataType: + def get_parameter_ask(self: Parameter, *args: Any) -> ParamRawDataType: # for some reason mypy does not understand # that cmd is a str even if this is defined inside # an if isinstance block From 1485b539f7e994ac8bd691667916ef4d8c71e88e Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 2 Aug 2024 15:55:44 +0200 Subject: [PATCH 13/15] Better todo --- src/qcodes/parameters/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index f58437b19f1d..af40d0349840 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -56,7 +56,7 @@ def set_parameter_write(self: Parameter, *args: Any) -> None: # that cmd is a str even if this is defined inside # an if isinstance block assert isinstance(cmd, str) - # TODO it is possible to format str with additional args. + # TODO it is possible to format str with more than one arg. # this does not seem to have been tested formatted_cmd = cmd.format(*args) return function(formatted_cmd) From 5a8f53cbd18c579c13616ef2e07ab84564aabf8e Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 24 Sep 2024 09:45:27 +0200 Subject: [PATCH 14/15] reformat --- src/qcodes/parameters/parameter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index af40d0349840..bff2605a7bd9 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -15,7 +15,6 @@ from .sweep_values import SweepFixedValues if TYPE_CHECKING: - from qcodes.instrument.base import InstrumentBase from qcodes.logger.instrument_logger import InstrumentLoggerAdapter from qcodes.validators import Validator From 1748622ba258654b794c5f4a98e9e944a92cdfc6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Thu, 24 Oct 2024 10:30:49 +0200 Subject: [PATCH 15/15] Add non working test --- tests/parameter/test_parameter_basics.py | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/parameter/test_parameter_basics.py b/tests/parameter/test_parameter_basics.py index a9264ab9b0cd..6944596b2529 100644 --- a/tests/parameter/test_parameter_basics.py +++ b/tests/parameter/test_parameter_basics.py @@ -1,6 +1,10 @@ +import logging +from typing import TYPE_CHECKING, Any + import pytest import qcodes.validators as vals +from qcodes.instrument import Instrument from qcodes.parameters import Function, Parameter, ParameterBase, ParamRawDataType from .conftest import ( @@ -10,6 +14,27 @@ named_instrument, ) +if TYPE_CHECKING: + from collections.abc import Generator + +_LOG = logging.getLogger(__name__) + + +class LoggingInstrument(Instrument): + def ask(self, cmd: str) -> Any: + _LOG.info(f"Received ask str {cmd}") + return 1 + + def write(self, cmd: str) -> None: + _LOG.info(f"Received write str {cmd}") + + +@pytest.fixture(name="logging_instrument") +def _make_logging_instrument() -> "Generator[LoggingInstrument, None, None]": + inst = LoggingInstrument("logging_instr") + yield inst + inst.close() + def test_no_name() -> None: with pytest.raises(TypeError): @@ -265,3 +290,43 @@ def test_set_cmd_str_no_instrument_raises() -> None: TypeError, match="Cannot use a str set_cmd without binding to an instrument." ): Parameter(name="test", instrument=None, set_cmd="set_me") + + +def test_str_get_set( + logging_instrument: LoggingInstrument, caplog: pytest.LogCaptureFixture +) -> None: + logging_instrument.add_parameter( + "my_param", get_cmd="my_param?", set_cmd="my_param{}" + ) + + caplog.clear() + + with caplog.at_level(logging.INFO): + logging_instrument.my_param.get() + + assert caplog.records[0].message == "Received ask str my_param?" + + caplog.clear() + + with caplog.at_level(logging.INFO): + logging_instrument.my_param.set(100) + + assert caplog.records[0].message == "Received write str my_param100" + + +def test_str_set_multi_arg( + logging_instrument: LoggingInstrument, caplog: pytest.LogCaptureFixture +) -> None: + logging_instrument.add_parameter("my_param", get_cmd="my_param?", set_cmd="{}{}") + + with caplog.at_level(logging.INFO): + logging_instrument.my_param.get() + + assert caplog.records[0].message == "Received ask str my_param?" + + caplog.clear() + + with caplog.at_level(logging.INFO): + logging_instrument.my_param.set("this_command", 2344) + + assert caplog.records[0].message == "Received write str this_command2344"