diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 5d9f2d68a3aa..eb62a78d7615 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -83,7 +83,7 @@ jobs: if: ${{ !matrix.min-version }} - uses: jakebailey/pyright-action@b8ffdb7aa4a15fab942303261611eaa541f3f8b0 # v2.2.1 with: - version: 1.1.349 + version: 1.1.351 if: ${{ !matrix.min-version }} - name: Run Mypy run: mypy -p qcodes diff --git a/docs/changes/newsfragments/5721.breaking b/docs/changes/newsfragments/5721.breaking new file mode 100644 index 000000000000..bc120ed755ef --- /dev/null +++ b/docs/changes/newsfragments/5721.breaking @@ -0,0 +1,9 @@ +QCoDeS is now type checked to ensure that subclasses are implemented in a way consistent with the parent class. +This has resulted in a number of changes to the API. The following is a list of the changes that have been made +to the API to make subclasses match their parent class. These changes are not expected to break any existing code, since they are +primarily in positional arguments or unused arguments. + +* The first argument to `NumpyJSONEncoder.default` has changed from `obj` to `o` to match the naming in the std library `json.JSONEncoder.default`. +* Unused args `idn_part` and `being_time` to `QDevQDac.connect_message` have been changed to `idn_param` and `begin_time` respectively to match the parent class. +* Unused arguments to stub methods `DSOTraceParam.setpoints`, `DSOTraceParam.unit` and `FormattedSweep.setpoints` have been changed to match the parent class. +* Alazar `DemodulationAcquisitionController.handle_buffer` the first argument has been renamed from `data` to `buffer` to match the parent class. diff --git a/pyproject.toml b/pyproject.toml index 6a1e7ef154de..c21e03eb19d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -209,13 +209,8 @@ ignore = [ reportMissingTypeStubs = true reportDeprecated = true stubPath = "typings/stubs" -# we would like to move this to at least standard -# eventually. From 1.1.339 onwards standard is the default -# for now we enable all of standard except for -# incompatibleMethodOverride which we have a lot of -typeCheckingMode = "standard" -reportIncompatibleMethodOverride = false +typeCheckingMode = "standard" [tool.pytest.ini_options] minversion = "7.2" diff --git a/src/qcodes/dataset/legacy_import.py b/src/qcodes/dataset/legacy_import.py index a07746f1521b..fb7f48c7fd64 100644 --- a/src/qcodes/dataset/legacy_import.py +++ b/src/qcodes/dataset/legacy_import.py @@ -58,7 +58,9 @@ def store_array_to_database(datasaver: DataSaver, array: DataArray) -> int: datasaver.add_result((array.set_arrays[0].array_id, i), (array.array_id, array[index])) else: - raise NotImplementedError('The exporter only currently handles 1 and 2 Dimentional data') + raise NotImplementedError( + "The exporter only currently handles 1 and 2 Dimensional data" + ) return datasaver.run_id @@ -67,7 +69,9 @@ def store_array_to_database_alt(meas: Measurement, array: DataArray) -> int: dims = len(array.shape) assert array.array_id is not None if dims == 2: - outer_data = np.empty(array.shape[1]) + outer_data = np.empty( + array.shape[1] # pyright: ignore[reportGeneralTypeIssues] + ) with meas.run() as datasaver: for index1, i in enumerate(array.set_arrays[0]): outer_data[:] = i @@ -80,7 +84,9 @@ def store_array_to_database_alt(meas: Measurement, array: DataArray) -> int: datasaver.add_result((array.set_arrays[0].array_id, i), (array.array_id, array[index])) else: - raise NotImplementedError('The exporter only currently handles 1 and 2 Dimentional data') + raise NotImplementedError( + "The exporter only currently handles 1 and 2 Dimensional data" + ) return datasaver.run_id diff --git a/src/qcodes/instrument/channel.py b/src/qcodes/instrument/channel.py index 3a55b2d218f4..6e18f034ae94 100644 --- a/src/qcodes/instrument/channel.py +++ b/src/qcodes/instrument/channel.py @@ -301,8 +301,13 @@ def name_parts(self) -> list[str]: name_parts.append(self.short_name) return name_parts - def index( - self, obj: InstrumentModuleType, start: int = 0, stop: int = sys.maxsize + # the parameter obj should be called value but that would + # be an incompatible change + def index( # pyright: ignore[reportIncompatibleMethodOverride] + self, + obj: InstrumentModuleType, + start: int = 0, + stop: int = sys.maxsize, ) -> int: """ Return the index of the given object @@ -314,7 +319,9 @@ def index( """ return self._channels.index(obj, start, stop) - def count(self, obj: InstrumentModuleType) -> int: + def count( # pyright: ignore[reportIncompatibleMethodOverride] + self, obj: InstrumentModuleType + ) -> int: """Returns number of instances of the given object in the list Args: @@ -625,7 +632,9 @@ def __setitem__( channel.short_name: channel for channel in self._channels } - def append(self, obj: InstrumentModuleType) -> None: + def append( # pyright: ignore[reportIncompatibleMethodOverride] + self, obj: InstrumentModuleType + ) -> None: """ Append a Channel to this list. Requires that the ChannelList is not locked and that the channel is of the same type as the ones in the list. @@ -654,7 +663,9 @@ def clear(self) -> None: self._channels.clear() self._channel_mapping.clear() - def remove(self, obj: InstrumentModuleType) -> None: + def remove( # pyright: ignore[reportIncompatibleMethodOverride] + self, obj: InstrumentModuleType + ) -> None: """ Removes obj from ChannelList if not locked. @@ -667,7 +678,9 @@ def remove(self, obj: InstrumentModuleType) -> None: self._channels.remove(obj) self._channel_mapping.pop(obj.short_name) - def extend(self, objects: Iterable[InstrumentModuleType]) -> None: + def extend( # pyright: ignore[reportIncompatibleMethodOverride] + self, objects: Iterable[InstrumentModuleType] + ) -> None: """ Insert an iterable of objects into the list of channels. @@ -685,7 +698,9 @@ def extend(self, objects: Iterable[InstrumentModuleType]) -> None: self._channels.extend(objects_tuple) self._channel_mapping.update({obj.short_name: obj for obj in objects_tuple}) - def insert(self, index: int, obj: InstrumentModuleType) -> None: + def insert( # pyright: ignore[reportIncompatibleMethodOverride] + self, index: int, obj: InstrumentModuleType + ) -> None: """ Insert an object into the ChannelList at a specific index. diff --git a/src/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/src/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index 25bd40b20b4f..51fa8b9071a6 100644 --- a/src/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/src/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -95,16 +95,14 @@ def pre_acquire(self) -> None: pass def handle_buffer( - self, - data: np.ndarray, - buffer_number: Optional[int] = None + self, buffer: np.ndarray, buffer_number: Optional[int] = None ) -> None: """ See AcquisitionController :return: """ assert self.buffer is not None - self.buffer += data + self.buffer += buffer def post_acquire(self) -> float: """ diff --git a/src/qcodes/instrument_drivers/AlazarTech/dll_wrapper.py b/src/qcodes/instrument_drivers/AlazarTech/dll_wrapper.py index 9e53c700d8c8..0cd914cbaf48 100644 --- a/src/qcodes/instrument_drivers/AlazarTech/dll_wrapper.py +++ b/src/qcodes/instrument_drivers/AlazarTech/dll_wrapper.py @@ -104,7 +104,9 @@ class DllWrapperMeta(type): # Only allow a single instance per DLL path. _instances: WeakValueDictionary[str, Any] = WeakValueDictionary() - def __call__(cls, dll_path: str, *args: Any, **kwargs: Any) -> Any: + def __call__( # pyright: ignore[reportIncompatibleMethodOverride] + cls, dll_path: str, *args: Any, **kwargs: Any + ) -> Any: api = cls._instances.get(dll_path, None) if api is not None: logger.debug( diff --git a/src/qcodes/instrument_drivers/Keysight/Infiniium.py b/src/qcodes/instrument_drivers/Keysight/Infiniium.py index 327a2abb59c0..cef27f21930e 100644 --- a/src/qcodes/instrument_drivers/Keysight/Infiniium.py +++ b/src/qcodes/instrument_drivers/Keysight/Infiniium.py @@ -135,7 +135,7 @@ def setpoints(self) -> Sequence[ParameterBase]: raise RuntimeError("Invalid type for parent instrument.") @setpoints.setter - def setpoints(self, val: Any) -> None: + def setpoints(self, setpoints: Any) -> None: """ Stub to allow initialization. Ignore any set attempts on setpoint as we figure it out on the fly. @@ -157,7 +157,7 @@ def unit(self) -> str: return "''" @unit.setter - def unit(self, val: Any) -> None: + def unit(self, unit: Any) -> None: """ Stub to allow initialization. """ diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py index 7f713f7c3070..afbf73203cc9 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py @@ -41,10 +41,15 @@ def get_raw(self) -> ParamRawDataType: class Trace(ParameterWithSetpoints): def __init__(self, number: int, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.instrument: ( + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] KeysightN9030BSpectrumAnalyzerMode | KeysightN9030BPhaseNoiseMode ) - self.root_instrument: KeysightN9030B + self.root_instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + KeysightN9030B + ) self.number = number diff --git a/src/qcodes/instrument_drivers/Keysight/KtM960x.py b/src/qcodes/instrument_drivers/Keysight/KtM960x.py index dd326510fc12..e93e44683738 100644 --- a/src/qcodes/instrument_drivers/Keysight/KtM960x.py +++ b/src/qcodes/instrument_drivers/Keysight/KtM960x.py @@ -23,7 +23,12 @@ def __init__(self, name: str, instrument: "KeysightM960x") -> None: instrument=instrument, labels="Measurement Data", docstring="param that returns measurement values") - self.instrument: "KeysightM960x" + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + "KeysightM960x" + ) def get_raw(self) -> tuple[ParamRawDataType, ...]: return self.instrument._measure() diff --git a/src/qcodes/instrument_drivers/Keysight/N52xx.py b/src/qcodes/instrument_drivers/Keysight/N52xx.py index c8cd6e810a69..dfdde9c9cc48 100644 --- a/src/qcodes/instrument_drivers/Keysight/N52xx.py +++ b/src/qcodes/instrument_drivers/Keysight/N52xx.py @@ -97,7 +97,7 @@ def setpoints(self) -> Sequence[ParameterBase]: raise NotImplementedError(f"Axis for type {sweep_type} not implemented yet") @setpoints.setter - def setpoints(self, val: Any) -> None: + def setpoints(self, setpoints: Any) -> None: """ Stub to allow initialization. Ignore any set attempts on setpoint as we figure it out on the fly. diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py index bdfd59ac8a2d..814d5aeb225f 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py @@ -461,9 +461,15 @@ def __init__(self, name: str, instrument: KeysightB1517A, **kwargs: Any): setpoint_units=(('V',),) * 2, instrument=instrument, **kwargs) - - self.instrument: KeysightB1517A - self.root_instrument: KeysightB1500 + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + KeysightB1517A + ) + self.root_instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + KeysightB1500 + ) self.param1 = _FMTResponse(None, None, None, None) self.param2 = _FMTResponse(None, None, None, None) diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_sampling_measurement.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_sampling_measurement.py index a911350e12ce..21c626bc8ca6 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_sampling_measurement.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_sampling_measurement.py @@ -36,9 +36,15 @@ class SamplingMeasurement(ParameterWithSetpoints): def __init__(self, name: str, **kwargs: Any): super().__init__(name, **kwargs) - - self.instrument: "KeysightB1517A" - self.root_instrument: "KeysightB1500" + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + "KeysightB1517A" + ) + self.root_instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + "KeysightB1500" + ) self.data = _FMTResponse(None, None, None, None) diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py index a4be1cd45b34..3dc7def8d0f4 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py @@ -884,9 +884,15 @@ def __init__(self, name: str, instrument: KeysightB1520A, **kwargs: Any): setpoint_units=(('V',),) * 2, instrument=instrument, **kwargs) - - self.instrument: "KeysightB1520A" - self.root_instrument: "KeysightB1500" + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + "KeysightB1520A" + ) + self.root_instrument: ( # pyright: ignore[reportIncompatibleMethodOverride] + "KeysightB1500" + ) self.update_name_label_unit_from_impedance_model() diff --git a/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py b/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py index 46fc76eec6db..a00c5c000b37 100644 --- a/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py @@ -320,8 +320,10 @@ class TimeTrace(ParameterWithSetpoints): """ def __init__(self, name: str, instrument: Instrument, **kwargs: Any): - - self.instrument: Instrument # needed for mypy + # the parameter classes should ideally be generic in instrument + # and root instrument classes so we can specialize here. + # for now we have to ignore a type error from pyright + self.instrument: Instrument # pyright: ignore[reportIncompatibleMethodOverride] super().__init__(name=name, instrument=instrument, **kwargs) # the extra time needed to avoid timeouts during acquisition diff --git a/src/qcodes/instrument_drivers/QDev/QDac_channels.py b/src/qcodes/instrument_drivers/QDev/QDac_channels.py index bbd5ab2a2e2d..2609d1659c7f 100644 --- a/src/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/src/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -687,9 +687,9 @@ def _wait_and_clear(self, delay: float = 0.5) -> None: time.sleep(delay) self.visa_handle.clear() - def connect_message(self, - idn_part: str = "IDN", - being_time: Optional[float] = None) -> None: + def connect_message( + self, idn_param: str = "IDN", begin_time: Optional[float] = None + ) -> None: """ Override of the standard Instrument class connect_message. Usually, the response to `*IDN?` is printed. Here, the diff --git a/src/qcodes/instrument_drivers/oxford/MercuryiPS_VISA.py b/src/qcodes/instrument_drivers/oxford/MercuryiPS_VISA.py index d1680fe3824e..0d88c43dc229 100644 --- a/src/qcodes/instrument_drivers/oxford/MercuryiPS_VISA.py +++ b/src/qcodes/instrument_drivers/oxford/MercuryiPS_VISA.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import logging import time from functools import partial -from typing import Any, Callable, Optional, Union, cast +from typing import Any, Callable, cast import numpy as np import numpy.typing as npt @@ -190,7 +192,7 @@ def _param_getter(self, get_cmd: str) -> str: return resp - def _param_setter(self, set_cmd: str, value: Union[float, str]) -> None: + def _param_setter(self, set_cmd: str, value: float | str) -> None: """ General setter function for parameters @@ -219,11 +221,14 @@ class OxfordMercuryiPS(VisaInstrument): supply """ - def __init__(self, name: str, address: str, visalib: Optional[str] = None, - field_limits: Optional[Callable[[float, - float, - float], bool]] = None, - **kwargs: Any) -> None: + def __init__( + self, + name: str, + address: str, + visalib: str | None = None, + field_limits: Callable[[float, float, float], bool] | None = None, + **kwargs: Any, + ) -> None: """ Args: name: The name to give this instrument internally in QCoDeS @@ -256,10 +261,6 @@ def __init__(self, name: str, address: str, visalib: Optional[str] = None, super().__init__(name, address, terminator='\n', visalib=visalib, **kwargs) - - # to ensure a correct snapshot, we must wrap the get function - self.IDN.get = self.IDN._wrap_get(self._idn_getter) - self.firmware = self.IDN()['firmware'] # TODO: Query instrument to ensure which PSUs are actually present @@ -351,7 +352,7 @@ def _set_ramp_rate(self, rate: FieldVector) -> None: self.GRPY.field_ramp_rate(rate.y) self.GRPZ.field_ramp_rate(rate.z) - def _get_measured(self, coordinates: list[str]) -> Union[float, list[float]]: + def _get_measured(self, coordinates: list[str]) -> float | list[float]: """ Get the measured value of a coordinate. Measures all three fields and computes whatever coordinate we asked for. @@ -401,7 +402,7 @@ def _set_target_field(self, field: FieldVector) -> None: for coord in 'xyz': self._set_target(coord, field[coord]) - def _idn_getter(self) -> dict[str, str]: + def get_idn(self) -> dict[str, str | None]: """ Parse the raw non-SCPI compliant IDN string into an IDN dict @@ -411,8 +412,12 @@ def _idn_getter(self) -> dict[str, str]: raw_idn_string = self.ask('*IDN?') resps = raw_idn_string.split(':') - idn_dict = {'model': resps[2], 'vendor': resps[1], - 'serial': resps[3], 'firmware': resps[4]} + idn_dict: dict[str, str | None] = { + "model": resps[2], + "vendor": resps[1], + "serial": resps[3], + "firmware": resps[4], + } return idn_dict diff --git a/src/qcodes/parameters/parameter.py b/src/qcodes/parameters/parameter.py index 81b563bf7984..8ad522a87036 100644 --- a/src/qcodes/parameters/parameter.py +++ b/src/qcodes/parameters/parameter.py @@ -5,6 +5,7 @@ import logging import os +from types import MethodType from typing import TYPE_CHECKING, Any, Callable, Literal from .command import Command @@ -180,7 +181,7 @@ def __init__( bind_to_instrument: bool = True, **kwargs: Any, ) -> None: - def _get_manual_parameter() -> ParamRawDataType: + def _get_manual_parameter(self: Parameter) -> ParamRawDataType: if self.root_instrument is not None: mylogger: InstrumentLoggerAdapter | logging.Logger = ( self.root_instrument.log @@ -194,7 +195,9 @@ def _get_manual_parameter() -> ParamRawDataType: ) return self.cache.raw_value - def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: + def _set_manual_parameter( + self: Parameter, x: ParamRawDataType + ) -> ParamRawDataType: if self.root_instrument is not None: mylogger: InstrumentLoggerAdapter | logging.Logger = ( self.root_instrument.log @@ -204,6 +207,7 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: 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: @@ -263,7 +267,8 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: ) elif not self.gettable and get_cmd is not False: if get_cmd is None: - self.get_raw: Callable[[], Any] = _get_manual_parameter + # 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( @@ -273,14 +278,16 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: ) exec_str_ask = getattr(instrument, "ask", None) if instrument else None - - self.get_raw = Command( + # 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._gettable = True - self.get = self._wrap_get(self.get_raw) + self.get = self._wrap_get() if self.settable and set_cmd not in (None, False): raise TypeError( @@ -290,7 +297,8 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: ) elif not self.settable and set_cmd is not False: if set_cmd is None: - self.set_raw: Callable[..., Any] = _set_manual_parameter + # 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( @@ -302,11 +310,14 @@ def _set_manual_parameter(x: ParamRawDataType) -> ParamRawDataType: exec_str_write = ( getattr(instrument, "write", None) if instrument else None ) - self.set_raw = Command( + # 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[method-assign] arg_count=1, cmd=set_cmd, exec_str=exec_str_write ) self._settable = True - self.set = self._wrap_set(self.set_raw) + self.set = self._wrap_set() self._meta_attrs.extend(["label", "unit", "vals"]) diff --git a/src/qcodes/parameters/parameter_base.py b/src/qcodes/parameters/parameter_base.py index de8299c7f330..0cfaa89dde78 100644 --- a/src/qcodes/parameters/parameter_base.py +++ b/src/qcodes/parameters/parameter_base.py @@ -257,7 +257,7 @@ def __init__( ) self._gettable = False if implements_get_raw: - self.get = self._wrap_get(self.get_raw) + self.get = self._wrap_get() self._gettable = True elif hasattr(self, "get"): raise RuntimeError( @@ -272,7 +272,7 @@ def __init__( ) self._settable: bool = False if implements_set_raw: - self.set = self._wrap_set(self.set_raw) + self.set = self._wrap_set() self._settable = True elif hasattr(self, "set"): raise RuntimeError( @@ -646,9 +646,9 @@ def _from_raw_value_to_value(self, raw_value: ParamRawDataType) -> ParamDataType return value def _wrap_get( - self, get_function: Callable[..., ParamDataType] + self, ) -> Callable[..., ParamDataType]: - @wraps(get_function) + @wraps(self.get_raw) def get_wrapper(*args: Any, **kwargs: Any) -> ParamDataType: if not self.gettable: raise TypeError("Trying to get a parameter that is not gettable.") @@ -658,7 +658,7 @@ def get_wrapper(*args: Any, **kwargs: Any) -> ParamDataType: ) try: # There might be cases where a .get also has args/kwargs - raw_value = get_function(*args, **kwargs) + raw_value = self.get_raw(*args, **kwargs) value = self._from_raw_value_to_value(raw_value) @@ -675,8 +675,10 @@ def get_wrapper(*args: Any, **kwargs: Any) -> ParamDataType: return get_wrapper - def _wrap_set(self, set_function: Callable[..., None]) -> Callable[..., None]: - @wraps(set_function) + def _wrap_set( + self, + ) -> Callable[..., None]: + @wraps(self.set_raw) def set_wrapper(value: ParamDataType, **kwargs: Any) -> None: try: if not self.settable: @@ -709,7 +711,7 @@ def set_wrapper(value: ParamDataType, **kwargs: Any) -> None: # Start timer to measure execution time of set_function t0 = time.perf_counter() - set_function(raw_val_step, **kwargs) + self.set_raw(raw_val_step, **kwargs) # Update last set time (used for calculating delays) self._t_last_set = time.perf_counter() diff --git a/src/qcodes/tests/dataset/conftest.py b/src/qcodes/tests/dataset/conftest.py index 9057aa9bc65c..f638577d0ef1 100644 --- a/src/qcodes/tests/dataset/conftest.py +++ b/src/qcodes/tests/dataset/conftest.py @@ -575,7 +575,7 @@ def SpectrumAnalyzer(): different types """ - class Spectrum(ArrayParameter): + class BaseSpectrum(ArrayParameter): def __init__(self, name, instrument, **kwargs): super().__init__( @@ -593,7 +593,7 @@ def __init__(self, name, instrument, **kwargs): self.start = 0 self.stop = 2e6 - def get_raw(self): + def get_data(self): # This is how it should be: the setpoints are generated at the # time we get. But that will of course not work with the old Loop self.setpoints = (tuple(np.linspace(self.start, self.stop, @@ -601,6 +601,10 @@ def get_raw(self): # not the best SA on the market; it just returns noise... return np.random.randn(self.npts) + class Spectrum(BaseSpectrum): + def get_raw(self): + return super().get_data() + class MultiDimSpectrum(ArrayParameter): def __init__(self, name, instrument, **kwargs): @@ -630,16 +634,16 @@ def __init__(self, name, instrument, **kwargs): def get_raw(self): return np.random.randn(*self.npts) - class ListSpectrum(Spectrum): + class ListSpectrum(BaseSpectrum): def get_raw(self): - output = super().get_raw() + output = super().get_data() return list(output) - class TupleSpectrum(Spectrum): + class TupleSpectrum(BaseSpectrum): def get_raw(self): - output = super().get_raw() + output = super().get_data() return tuple(output) SA = DummyInstrument('dummy_SA') diff --git a/src/qcodes/utils/json_utils.py b/src/qcodes/utils/json_utils.py index f8bde2a95c6e..451476675445 100644 --- a/src/qcodes/utils/json_utils.py +++ b/src/qcodes/utils/json_utils.py @@ -13,7 +13,7 @@ class NumpyJSONEncoder(json.JSONEncoder): ``default`` method for the description of all conversions. """ - def default(self, obj: Any) -> Any: + def default(self, o: Any) -> Any: """ List of conversions that this encoder performs: @@ -41,50 +41,50 @@ def default(self, obj: Any) -> Any: """ import uncertainties # type: ignore[import-untyped] - if isinstance(obj, np.generic) and not isinstance(obj, np.complexfloating): + if isinstance(o, np.generic) and not isinstance(o, np.complexfloating): # for numpy scalars - return obj.item() - elif isinstance(obj, np.ndarray): + return o.item() + elif isinstance(o, np.ndarray): # for numpy arrays - return obj.tolist() - elif isinstance(obj, numbers.Complex) and not isinstance(obj, numbers.Real): + return o.tolist() + elif isinstance(o, numbers.Complex) and not isinstance(o, numbers.Real): return { "__dtype__": "complex", - "re": float(obj.real), - "im": float(obj.imag), + "re": float(o.real), + "im": float(o.imag), } - elif isinstance(obj, uncertainties.UFloat): + elif isinstance(o, uncertainties.UFloat): return { "__dtype__": "UFloat", - "nominal_value": float(obj.nominal_value), - "std_dev": float(obj.std_dev), + "nominal_value": float(o.nominal_value), + "std_dev": float(o.std_dev), } - elif hasattr(obj, "_JSONEncoder"): + elif hasattr(o, "_JSONEncoder"): # Use object's custom JSON encoder - jsosencode = getattr(obj, "_JSONEncoder") + jsosencode = getattr(o, "_JSONEncoder") return jsosencode() else: try: - s = super().default(obj) + s = super().default(o) except TypeError: # json does not support dumping UserDict but # we can dump the dict stored internally in the # UserDict - if isinstance(obj, collections.UserDict): - return obj.data + if isinstance(o, collections.UserDict): + return o.data # See if the object supports the pickle protocol. # If so, we should be able to use that to serialize. # __getnewargs__ will return bytes for a bytes object # causing an infinte recursion, so we do not # try to pickle bytes or bytearrays - if hasattr(obj, "__getnewargs__") and not isinstance( - obj, (bytes, bytearray) + if hasattr(o, "__getnewargs__") and not isinstance( + o, (bytes, bytearray) ): return { - "__class__": type(obj).__name__, - "__args__": getattr(obj, "__getnewargs__")(), + "__class__": type(o).__name__, + "__args__": getattr(o, "__getnewargs__")(), } else: # we cannot convert the object to JSON, just take a string - s = str(obj) + s = str(o) return s diff --git a/tests/dataset/conftest.py b/tests/dataset/conftest.py index 9057aa9bc65c..499b444de305 100644 --- a/tests/dataset/conftest.py +++ b/tests/dataset/conftest.py @@ -575,7 +575,7 @@ def SpectrumAnalyzer(): different types """ - class Spectrum(ArrayParameter): + class BaseSpectrum(ArrayParameter): def __init__(self, name, instrument, **kwargs): super().__init__( @@ -588,12 +588,11 @@ def __init__(self, name, instrument, **kwargs): instrument=instrument, **kwargs, ) - self.npts = 100 self.start = 0 self.stop = 2e6 - def get_raw(self): + def get_data(self): # This is how it should be: the setpoints are generated at the # time we get. But that will of course not work with the old Loop self.setpoints = (tuple(np.linspace(self.start, self.stop, @@ -601,6 +600,10 @@ def get_raw(self): # not the best SA on the market; it just returns noise... return np.random.randn(self.npts) + class Spectrum(BaseSpectrum): + def get_raw(self): + return super().get_data() + class MultiDimSpectrum(ArrayParameter): def __init__(self, name, instrument, **kwargs): @@ -630,16 +633,16 @@ def __init__(self, name, instrument, **kwargs): def get_raw(self): return np.random.randn(*self.npts) - class ListSpectrum(Spectrum): + class ListSpectrum(BaseSpectrum): def get_raw(self): - output = super().get_raw() + output = super().get_data() return list(output) - class TupleSpectrum(Spectrum): + class TupleSpectrum(BaseSpectrum): def get_raw(self): - output = super().get_raw() + output = super().get_data() return tuple(output) SA = DummyInstrument('dummy_SA') diff --git a/tests/parameter/test_get_latest.py b/tests/parameter/test_get_latest.py index bef50531bc46..f0763c8087f0 100644 --- a/tests/parameter/test_get_latest.py +++ b/tests/parameter/test_get_latest.py @@ -170,7 +170,7 @@ class LocalParameter(ParameterBase): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self.set_raw = lambda x: x # type: ignore[method-assign] - self.set = self._wrap_set(self.set_raw) + self.set = self._wrap_set() localparameter = LocalParameter('test_param', None, diff --git a/tests/parameter/test_manual_parameter.py b/tests/parameter/test_manual_parameter.py new file mode 100644 index 000000000000..48790f99b59b --- /dev/null +++ b/tests/parameter/test_manual_parameter.py @@ -0,0 +1,19 @@ +from qcodes.parameters import Parameter + + +def test_manual_parameter_set_get_raw() -> None: + myparam = Parameter("myparam", set_cmd=None, get_cmd=None) + + value = 23 + value2 = 132 + + assert myparam.get() is None + assert myparam.get_raw() is None + + myparam.set_raw(value) + assert myparam.get() == value + assert myparam.get_raw() == value + + myparam.set(value2) + assert myparam.get() == value2 + assert myparam.get_raw() == value2 diff --git a/tests/parameter/test_parameter_cache.py b/tests/parameter/test_parameter_cache.py index 918fa2793c9d..bf473a8b8bb6 100644 --- a/tests/parameter/test_parameter_cache.py +++ b/tests/parameter/test_parameter_cache.py @@ -230,7 +230,7 @@ class LocalParameter(ParameterBase): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self.set_raw = lambda x: x # type: ignore[method-assign] - self.set = self._wrap_set(self.set_raw) + self.set = self._wrap_set() local_parameter = LocalParameter('test_param', None,