Skip to content

Commit 6b8c257

Browse files
authored
Merge pull request #6676 from jenshnielsen/backport_6671
Backport #6671
2 parents 4ad5c78 + c5d1c21 commit 6b8c257

File tree

5 files changed

+49
-13
lines changed

5 files changed

+49
-13
lines changed

docs/changes/0.50.1.rst

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
QCoDeS 0.50.1 (2024-11-28)
2+
==========================
3+
4+
Improved:
5+
---------
6+
7+
- Fix a regression introduced in 0.50.0 where a DelegateParameter initialized with a None source
8+
would not correctly call get/set on the source parameter when this has been set. (:pr:`6671`)

docs/changes/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changelogs
33

44
.. toctree::
55
Unreleased <unreleased>
6+
0.50.1 <0.50.1>
67
0.50.0 <0.50.0>
78
0.49.0 <0.49.0>
89
0.48.0 <0.48.0>

src/qcodes/parameters/parameter.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ def _set_manual_parameter(
245245
**kwargs,
246246
)
247247

248-
no_instrument_get = not self.gettable and (get_cmd is None or get_cmd is False)
248+
no_instrument_get = not self._implements_get_raw and (
249+
get_cmd is None or get_cmd is False
250+
)
249251
# TODO: a matching check should be in ParameterBase but
250252
# due to the current limited design the ParameterBase cannot
251253
# know if this subclass will supply a get_cmd
@@ -261,13 +263,13 @@ def _set_manual_parameter(
261263
# in the scope of this class.
262264
# (previous call to `super().__init__` wraps existing
263265
# get_raw/set_raw into get/set methods)
264-
if self.gettable and get_cmd not in (None, False):
266+
if self._implements_get_raw and get_cmd not in (None, False):
265267
raise TypeError(
266268
"Supplying a not None or False `get_cmd` to a Parameter"
267269
" that already implements"
268270
" get_raw is an error."
269271
)
270-
elif not self.gettable and get_cmd is not False:
272+
elif not self._implements_get_raw and get_cmd is not False:
271273
if get_cmd is None:
272274
# ignore typeerror since mypy does not allow setting a method dynamically
273275
self.get_raw = MethodType(_get_manual_parameter, self) # type: ignore[method-assign]
@@ -293,13 +295,13 @@ def _set_manual_parameter(
293295
# this may be resolvable if Command above is correctly wrapped in MethodType
294296
self.get = self._wrap_get(self.get_raw) # type: ignore[arg-type]
295297

296-
if self.settable and set_cmd not in (None, False):
298+
if self._implements_set_raw and set_cmd not in (None, False):
297299
raise TypeError(
298300
"Supplying a not None or False `set_cmd` to a Parameter"
299301
" that already implements"
300302
" set_raw is an error."
301303
)
302-
elif not self.settable and set_cmd is not False:
304+
elif not self._implements_set_raw and set_cmd is not False:
303305
if set_cmd is None:
304306
# ignore typeerror since mypy does not allow setting a method dynamically
305307
self.set_raw = MethodType(_set_manual_parameter, self) # type: ignore[method-assign]

src/qcodes/parameters/parameter_base.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,8 @@ def __init__(
263263
self.get_latest = GetLatest(self)
264264

265265
self.get: Callable[..., ParamDataType]
266-
implements_get_raw = hasattr(self, "get_raw") and not getattr(
267-
self.get_raw, "__qcodes_is_abstract_method__", False
268-
)
269266
self._gettable = False
270-
if implements_get_raw:
267+
if self._implements_get_raw:
271268
self.get = self._wrap_get(self.get_raw)
272269
self._gettable = True
273270
elif hasattr(self, "get"):
@@ -278,11 +275,8 @@ def __init__(
278275
)
279276

280277
self.set: Callable[..., None]
281-
implements_set_raw = hasattr(self, "set_raw") and not getattr(
282-
self.set_raw, "__qcodes_is_abstract_method__", False
283-
)
284278
self._settable: bool = False
285-
if implements_set_raw:
279+
if self._implements_set_raw:
286280
self.set = self._wrap_set(self.set_raw)
287281
self._settable = True
288282
elif hasattr(self, "set"):
@@ -347,6 +341,20 @@ def __init__(
347341

348342
instrument.parameters[name] = self
349343

344+
@property
345+
def _implements_get_raw(self) -> bool:
346+
implements_get_raw = hasattr(self, "get_raw") and not getattr(
347+
self.get_raw, "__qcodes_is_abstract_method__", False
348+
)
349+
return implements_get_raw
350+
351+
@property
352+
def _implements_set_raw(self) -> bool:
353+
implements_set_raw = hasattr(self, "set_raw") and not getattr(
354+
self.set_raw, "__qcodes_is_abstract_method__", False
355+
)
356+
return implements_set_raw
357+
350358
def _build__doc__(self) -> str | None:
351359
return self.__doc__
352360

tests/parameter/test_delegate_parameter.py

+17
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,23 @@ def test_delegate_cache_pristine_if_not_set() -> None:
271271
assert gotten_delegate_cache is None
272272

273273

274+
def test_delegate_get_instrument_val(numeric_val: int) -> None:
275+
"""
276+
Delegate should call its source to get value rather than just reading source cache
277+
"""
278+
initial_value = numeric_val
279+
t = ObservableParam("observable_parameter", initial_value=initial_value)
280+
# delegate has no source initially to make sure it's not gettable on initialization
281+
d = DelegateParameter("delegate", source=None)
282+
d.source = t
283+
284+
new_instr_value = 3
285+
# Update instrument value without changing parameter cache
286+
t.instr_val = new_instr_value
287+
# This check fails if delegate only reads source cache
288+
assert d() == new_instr_value
289+
290+
274291
def test_delegate_get_updates_cache(
275292
make_observable_parameter: Callable[..., ObservableParam], numeric_val: int
276293
) -> None:

0 commit comments

Comments
 (0)