From 83f13b1b9c2504bb0fa2925be3f618874dedc77a Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 14 Mar 2026 07:56:38 +0100 Subject: [PATCH 1/5] Strict type checking of Minicircuits --- .../instrument_drivers/Minicircuits/Base_SPDT.py | 11 +++++++---- .../Minicircuits/_minicircuits_rc_sp4t.py | 9 +++++---- .../Minicircuits/_minicircuits_usb_spdt.py | 8 +++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/qcodes/instrument_drivers/Minicircuits/Base_SPDT.py b/src/qcodes/instrument_drivers/Minicircuits/Base_SPDT.py index 37a75d38a471..193b6bac010d 100644 --- a/src/qcodes/instrument_drivers/Minicircuits/Base_SPDT.py +++ b/src/qcodes/instrument_drivers/Minicircuits/Base_SPDT.py @@ -2,7 +2,7 @@ import logging import re -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Generic, TypeVar from qcodes.instrument import ( ChannelList, @@ -19,11 +19,13 @@ log = logging.getLogger(__name__) +_TINSTR = TypeVar("_TINSTR", bound="MiniCircuitsSPDTBase") -class MiniCircuitsSPDTSwitchChannelBase(InstrumentChannel): + +class MiniCircuitsSPDTSwitchChannelBase(InstrumentChannel[_TINSTR], Generic[_TINSTR]): def __init__( self, - parent: Instrument, + parent: _TINSTR, name: str, channel_letter: str, **kwargs: Unpack[InstrumentBaseKWArgs], @@ -89,7 +91,8 @@ def add_channels(self) -> None: channel = self.CHANNEL_CLASS(self, f"channel_{c}", c) channels.append(channel) self.add_submodule(c, channel) - self.add_submodule("channels", channels.to_channel_tuple()) + self.channels = self.add_submodule("channels", channels.to_channel_tuple()) + """Channel list containing all channels of the switch""" def all(self, switch_to: int) -> None: for c in self.channels: diff --git a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py index 4acc8aa51225..87dfcd664c7b 100644 --- a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py +++ b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py @@ -15,10 +15,10 @@ from qcodes.parameters import Parameter -class MiniCircuitsRCSP4TChannel(InstrumentChannel): +class MiniCircuitsRCSP4TChannel(InstrumentChannel["MiniCircuitsRCSP4T"]): def __init__( self, - parent: IPInstrument, + parent: "MiniCircuitsRCSP4T", name: str, channel_letter: str, **kwargs: "Unpack[InstrumentBaseKWArgs]", @@ -47,7 +47,7 @@ def __init__( """Parameter switch""" def _set_switch(self, switch: int) -> None: - if len(self._parent.channels) > 1: + if len(self.parent.channels) > 1: current_switchstate = int(self.ask("SWPORT?")) mask = 0xF << (4 * (1 - self.channel_number)) current_switchstate = current_switchstate & mask @@ -111,7 +111,8 @@ def __init__( channel = MiniCircuitsRCSP4TChannel(self, f"channel_{c}", c) channels.append(channel) self.add_submodule(f"channel_{c}", channel) - self.add_submodule("channels", channels.to_channel_tuple()) + self.channels = self.add_submodule("channels", channels.to_channel_tuple()) + """ChannelTuple of channels""" self.connect_message() diff --git a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_usb_spdt.py b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_usb_spdt.py index 10265c324a26..206a9531c7a4 100644 --- a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_usb_spdt.py +++ b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_usb_spdt.py @@ -22,12 +22,14 @@ ) -class MiniCircuitsUsbSPDTSwitchChannel(MiniCircuitsSPDTSwitchChannelBase): +class MiniCircuitsUsbSPDTSwitchChannel( + MiniCircuitsSPDTSwitchChannelBase["MiniCircuitsUsbSPDT"] +): def _set_switch(self, switch: int) -> None: - self._parent.switch.Set_Switch(self.channel_letter, switch - 1) + self.parent.switch.Set_Switch(self.channel_letter, switch - 1) def _get_switch(self) -> int: - status = self._parent.switch.GetSwitchesStatus(self._parent.address)[1] + status = self.parent.switch.GetSwitchesStatus(self._parent.address)[1] return int(f"{status:04b}"[-1 - self.channel_number]) + 1 From bafa8308096cd50696975df0246e7440015058e6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 3 Feb 2026 21:41:38 +0100 Subject: [PATCH 2/5] Fix unresolved types in Keysight KtMAwg --- src/qcodes/instrument_drivers/Keysight/KtMAwg.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py index 309a0aebacf9..f1c36735ae98 100644 --- a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py +++ b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py @@ -12,7 +12,7 @@ from typing_extensions import Unpack -class KeysightM9336AAWGChannel(InstrumentChannel): +class KeysightM9336AAWGChannel(InstrumentChannel["KeysightM9336A"]): """ Represent the three channels of the Keysight KTM Awg driver. The channels can be independently controlled and programmed with @@ -135,6 +135,12 @@ def __init__( ) """Parameter digital_gain""" + @property + def root_instrument(self) -> "KeysightM9336A": + root_instrument = super().root_instrument + assert isinstance(root_instrument, KeysightM9336A) + return root_instrument + def load_waveform(self, filename: str) -> None: path = ctypes.create_string_buffer(filename.encode("ascii")) self._awg_handle = ctypes.c_int32(-1) From 4aab8b60261c74eb00cbf9e109029fc0d9bdc4f6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 14 Mar 2026 08:10:36 +0100 Subject: [PATCH 3/5] Strict lakeshorebase --- .../instrument_drivers/Lakeshore/lakeshore_base.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py index fa2f68315d6a..433090d13897 100644 --- a/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py +++ b/src/qcodes/instrument_drivers/Lakeshore/lakeshore_base.py @@ -23,7 +23,7 @@ from qcodes.instrument.channel import ChannelTuple -class LakeshoreBaseOutput(InstrumentChannel): +class LakeshoreBaseOutput(InstrumentChannel["LakeshoreBase"]): MODES: ClassVar[dict[str, int]] = {} RANGES: ClassVar[dict[str, int]] = {} @@ -475,9 +475,11 @@ def wait_until_set_point_reached( ) active_channel_id = self.input_channel() - active_channel_name_on_instrument = self.root_instrument.input_channel_parameter_values_to_channel_name_on_instrument[ - active_channel_id - ] + active_channel_name_on_instrument = ( + self.parent.input_channel_parameter_values_to_channel_name_on_instrument[ + active_channel_id + ] + ) active_channel = getattr( self.root_instrument, active_channel_name_on_instrument ) From ea9f77a32369ab42ebc376aa64835e107afb080e Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 14 Mar 2026 08:16:43 +0100 Subject: [PATCH 4/5] Fix KtMAwg type check --- .../instrument_drivers/Keysight/KtMAwg.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py index f1c36735ae98..1a6ba5d92c79 100644 --- a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py +++ b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py @@ -12,6 +12,9 @@ from typing_extensions import Unpack +_ch_type = bytes | ctypes.Array[ctypes.c_char] + + class KeysightM9336AAWGChannel(InstrumentChannel["KeysightM9336A"]): """ Represent the three channels of the Keysight KTM Awg driver. @@ -347,7 +350,7 @@ def get_errors(self) -> dict[int, str]: return error_dict # Generic functions for reading/writing different attributes - def _get_vi_string(self, attr: int, ch: bytes = b"") -> str: + def _get_vi_string(self, attr: int, ch: _ch_type = b"") -> str: s = ctypes.create_string_buffer(self._default_buf_size) status = self._dll.KtMAwg_GetAttributeViString( self._session, ch, attr, self._default_buf_size, s @@ -356,7 +359,7 @@ def _get_vi_string(self, attr: int, ch: bytes = b"") -> str: raise ValueError(f"Driver error: {status}") return s.value.decode("utf-8") - def _get_vi_bool(self, attr: int, ch: bytes = b"") -> bool: + def _get_vi_bool(self, attr: int, ch: _ch_type = b"") -> bool: s = ctypes.c_uint16(0) status = self._dll.KtMAwg_GetAttributeViBoolean( self._session, ch, attr, ctypes.byref(s) @@ -365,13 +368,13 @@ def _get_vi_bool(self, attr: int, ch: bytes = b"") -> bool: raise ValueError(f"Driver error: {status}") return bool(s) - def _set_vi_bool(self, attr: int, value: bool, ch: bytes = b"") -> None: + def _set_vi_bool(self, attr: int, value: bool, ch: _ch_type = b"") -> None: v = ctypes.c_uint16(1) if value else ctypes.c_uint16(0) status = self._dll.KtMAwg_SetAttributeViBoolean(self._session, ch, attr, v) if status: raise ValueError(f"Driver error: {status}") - def _get_vi_real64(self, attr: int, ch: bytes = b"") -> float: + def _get_vi_real64(self, attr: int, ch: _ch_type = b"") -> float: s = ctypes.c_double(0) status = self._dll.KtMAwg_GetAttributeViReal64( self._session, ch, attr, ctypes.byref(s) @@ -381,19 +384,19 @@ def _get_vi_real64(self, attr: int, ch: bytes = b"") -> float: raise ValueError(f"Driver error: {status}") return float(s.value) - def _set_vi_real64(self, attr: int, value: float, ch: bytes = b"") -> None: + def _set_vi_real64(self, attr: int, value: float, ch: _ch_type = b"") -> None: v = ctypes.c_double(value) status = self._dll.KtMAwg_SetAttributeViReal64(self._session, ch, attr, v) if status: raise ValueError(f"Driver error: {status}") - def _set_vi_int(self, attr: int, value: int, ch: bytes = b"") -> None: + def _set_vi_int(self, attr: int, value: int, ch: _ch_type = b"") -> None: v = ctypes.c_int32(value) status = self._dll.KtMAwg_SetAttributeViInt32(self._session, ch, attr, v) if status: raise ValueError(f"Driver error: {status}") - def _get_vi_int(self, attr: int, ch: bytes = b"") -> int: + def _get_vi_int(self, attr: int, ch: _ch_type = b"") -> int: v = ctypes.c_int32(0) status = self._dll.KtMAwg_GetAttributeViInt32( self._session, ch, attr, ctypes.byref(v) From 4a0ea6635f50ff53fbb382227949f62d9861c3e0 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 14 Mar 2026 08:21:41 +0100 Subject: [PATCH 5/5] Stricter stahl --- src/qcodes/instrument_drivers/stahl/stahl.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/qcodes/instrument_drivers/stahl/stahl.py b/src/qcodes/instrument_drivers/stahl/stahl.py index be300f5e7e76..afa3637fe841 100644 --- a/src/qcodes/instrument_drivers/stahl/stahl.py +++ b/src/qcodes/instrument_drivers/stahl/stahl.py @@ -57,12 +57,12 @@ def inner(*args: Any) -> Any: return inner -class StahlChannel(InstrumentChannel): +class StahlChannel(InstrumentChannel["Stahl"]): acknowledge_reply = chr(6) def __init__( self, - parent: VisaInstrument, + parent: "Stahl", name: str, channel_number: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", @@ -215,7 +215,10 @@ def __init__( self.add_submodule(name, channel) channels.append(channel) - self.add_submodule("channel", channels) + self.channels: ChannelList[StahlChannel] = self.add_submodule( + "channel", channels + ) + """ChannelList of channels""" self.temperature: Parameter = self.add_parameter( "temperature",