Skip to content

Commit 8eef902

Browse files
committed
Avoid using Command for get
1 parent f226a90 commit 8eef902

File tree

2 files changed

+76
-43
lines changed

2 files changed

+76
-43
lines changed

src/qcodes/parameters/parameter.py

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
import logging
77
import os
8-
from functools import wraps
98
from types import MethodType
10-
from typing import TYPE_CHECKING, Any, Callable, Literal
9+
from typing import TYPE_CHECKING, Any, Callable, Literal, cast
10+
11+
from qcodes.utils import is_function
1112

1213
from .command import Command
1314
from .parameter_base import ParamDataType, ParameterBase, ParamRawDataType
@@ -22,6 +23,65 @@
2223
log = logging.getLogger(__name__)
2324

2425

26+
def _get_parameter_factory(
27+
function: Callable[[str], ParamRawDataType] | None,
28+
cmd: str | Callable[[], ParamRawDataType] | None,
29+
parameter_name: str,
30+
) -> Callable[[Parameter], ParamRawDataType]:
31+
if cmd is None:
32+
33+
def get_manual_parameter(self: Parameter) -> ParamRawDataType:
34+
if self.root_instrument is not None:
35+
mylogger: InstrumentLoggerAdapter | logging.Logger = (
36+
self.root_instrument.log
37+
)
38+
else:
39+
mylogger = log
40+
mylogger.debug(
41+
"Getting raw value of parameter: %s as %s",
42+
self.full_name,
43+
self.cache.raw_value,
44+
)
45+
return self.cache.raw_value
46+
47+
return get_manual_parameter
48+
49+
elif isinstance(cmd, str) and is_function(function, 1):
50+
# cast is safe since we just checked this above using is_function
51+
function = cast(Callable[[str], ParamRawDataType], function)
52+
53+
def get_parameter_ask(self: Parameter) -> ParamRawDataType:
54+
# for some reason mypy does not understand
55+
# that cmd is a str even if this is defined inside
56+
# an if isinstance block
57+
assert isinstance(cmd, str)
58+
return function(cmd)
59+
60+
return get_parameter_ask
61+
62+
elif is_function(cmd, 0):
63+
# cast is safe since we just checked this above using is_function
64+
cmd = cast(Callable[[], ParamRawDataType], cmd)
65+
66+
def get_parameter_func(self: Parameter) -> ParamRawDataType:
67+
return cmd()
68+
69+
return get_parameter_func
70+
71+
elif isinstance(cmd, str) and function is None:
72+
raise TypeError(
73+
f"Cannot use a str get_cmd without "
74+
f"binding to an instrument. "
75+
f"Got: get_cmd {cmd} for parameter {parameter_name}"
76+
)
77+
78+
else:
79+
raise TypeError(
80+
"Unexpected options for parameter get. "
81+
f"Got: get_cmd {cmd} for parameter {parameter_name}"
82+
)
83+
84+
2585
class Parameter(ParameterBase):
2686
"""
2787
A parameter represents a single degree of freedom. Most often,
@@ -172,7 +232,7 @@ def __init__(
172232
instrument: InstrumentBase | None = None,
173233
label: str | None = None,
174234
unit: str | None = None,
175-
get_cmd: str | Callable[..., Any] | Literal[False] | None = None,
235+
get_cmd: str | Callable[[], ParamRawDataType] | Literal[False] | None = None,
176236
set_cmd: str | Callable[..., Any] | Literal[False] | None = False,
177237
initial_value: float | str | None = None,
178238
max_val_age: float | None = None,
@@ -211,25 +271,10 @@ def _set_manual_parameter(
211271
self.cache._set_from_raw_value(x)
212272
return x
213273

214-
def _get_command_caller(parameter: Parameter, command: Command) -> MethodType:
215-
@wraps(Command.__call__)
216-
def call_command(self: Parameter) -> Any:
217-
return command()
218-
219-
return MethodType(call_command, parameter)
220-
221-
def _set_command_caller(parameter: Parameter, command: Command) -> MethodType:
222-
@wraps(Command.__call__)
223-
def call_command(self: Parameter, val: ParamRawDataType) -> Any:
224-
return command(val)
225-
226-
return MethodType(call_command, parameter)
227-
228274
if instrument is not None and bind_to_instrument:
229275
existing_parameter = instrument.parameters.get(name, None)
230276

231277
if existing_parameter:
232-
233278
# this check is redundant since its also in the baseclass
234279
# but if we do not put it here it would be an api break
235280
# as parameter duplication check won't be done first,
@@ -281,27 +326,15 @@ def call_command(self: Parameter, val: ParamRawDataType) -> Any:
281326
" get_raw is an error."
282327
)
283328
elif not self.gettable and get_cmd is not False:
284-
if get_cmd is None:
285-
# ignore typeerror since mypy does not allow setting a method dynamically
286-
self.get_raw = MethodType(_get_manual_parameter, self) # type: ignore[method-assign]
287-
else:
288-
if isinstance(get_cmd, str) and instrument is None:
289-
raise TypeError(
290-
f"Cannot use a str get_cmd without "
291-
f"binding to an instrument. "
292-
f"Got: get_cmd {get_cmd} for parameter {name}"
293-
)
329+
exec_str_ask: Callable[[str], ParamRawDataType] | None = (
330+
getattr(instrument, "ask", None) if instrument else None
331+
)
332+
333+
self.get_raw = MethodType( # type: ignore[method-assign]
334+
_get_parameter_factory(exec_str_ask, cmd=get_cmd, parameter_name=name),
335+
self,
336+
)
294337

295-
exec_str_ask = getattr(instrument, "ask", None) if instrument else None
296-
# ignore typeerror since mypy does not allow setting a method dynamically
297-
self.get_raw = _get_command_caller( # type: ignore[method-assign]
298-
self,
299-
Command(
300-
arg_count=0,
301-
cmd=get_cmd,
302-
exec_str=exec_str_ask,
303-
),
304-
)
305338
self._gettable = True
306339
self.get = self._wrap_get(self.get_raw)
307340

@@ -326,10 +359,11 @@ def call_command(self: Parameter, val: ParamRawDataType) -> Any:
326359
exec_str_write = (
327360
getattr(instrument, "write", None) if instrument else None
328361
)
362+
# TODO get_raw should also be a method here. This should probably be done by wrapping
363+
# it with MethodType like above
329364
# ignore typeerror since mypy does not allow setting a method dynamically
330-
self.set_raw = _set_command_caller( # type: ignore[method-assign]
331-
self,
332-
Command(arg_count=1, cmd=set_cmd, exec_str=exec_str_write),
365+
self.set_raw = Command( # type: ignore[assignment]
366+
arg_count=1, cmd=set_cmd, exec_str=exec_str_write
333367
)
334368
self._settable = True
335369
self.set = self._wrap_set(self.set_raw)

src/qcodes/utils/function_helpers.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,5 @@ def is_function(f: object, arg_count: int, coroutine: bool = False) -> bool:
4040
inputs = [0] * arg_count
4141
sig.bind(*inputs)
4242
return True
43-
except TypeError as e:
44-
raise e
43+
except TypeError:
4544
return False

0 commit comments

Comments
 (0)