From e7080035e5719807c05d89251d3615b4bb8841a9 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 27 Apr 2024 11:02:29 +0200 Subject: [PATCH 01/23] Add typed kwargs to Instrument/InstrumentBase --- src/qcodes/instrument/__init__.py | 3 ++- src/qcodes/instrument/instrument.py | 14 ++++------- src/qcodes/instrument/instrument_base.py | 7 ++++++ src/qcodes/instrument/ip.py | 18 ++++++--------- src/qcodes/instrument/visa.py | 7 ++++-- .../Minicircuits/_minicircuits_rc_sp4t.py | 23 +++++++++++++++---- .../Minicircuits/_minicircuits_rc_spdt.py | 22 ++++++++++++++---- .../mock_instruments/__init__.py | 21 +++++++++++++---- 8 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/qcodes/instrument/__init__.py b/src/qcodes/instrument/__init__.py index 99920a876b5..3cc1aec4f96 100644 --- a/src/qcodes/instrument/__init__.py +++ b/src/qcodes/instrument/__init__.py @@ -17,7 +17,7 @@ from .channel import ChannelList, ChannelTuple, InstrumentChannel, InstrumentModule from .instrument import Instrument, find_or_create_instrument -from .instrument_base import InstrumentBase +from .instrument_base import InstrumentBase, InstrumentBaseKWArgs from .ip import IPInstrument from .visa import VisaInstrument @@ -27,6 +27,7 @@ "IPInstrument", "Instrument", "InstrumentBase", + "InstrumentBaseKWArgs", "InstrumentChannel", "InstrumentModule", "VisaInstrument", diff --git a/src/qcodes/instrument/instrument.py b/src/qcodes/instrument/instrument.py index a65311ec5aa..33dda4982d3 100644 --- a/src/qcodes/instrument/instrument.py +++ b/src/qcodes/instrument/instrument.py @@ -9,15 +9,14 @@ from qcodes.utils import strip_attrs from qcodes.validators import Anything -from .instrument_base import InstrumentBase +from .instrument_base import InstrumentBase, InstrumentBaseKWArgs from .instrument_meta import InstrumentMeta if TYPE_CHECKING: - from collections.abc import Mapping + from typing_extensions import Unpack from qcodes.logger.instrument_logger import InstrumentLoggerAdapter - log = logging.getLogger(__name__) @@ -66,16 +65,11 @@ class Instrument(InstrumentBase, metaclass=instrument_meta_class): _type: type[Instrument] | None = None _instances: weakref.WeakSet[Instrument] = weakref.WeakSet() - def __init__( - self, - name: str, - metadata: Mapping[Any, Any] | None = None, - label: str | None = None, - ) -> None: + def __init__(self, name: str, **kwargs: Unpack[InstrumentBaseKWArgs]) -> None: self._t0 = time.time() - super().__init__(name=name, metadata=metadata, label=label) + super().__init__(name=name, **kwargs) self.add_parameter("IDN", get_cmd=self.get_idn, vals=Anything()) diff --git a/src/qcodes/instrument/instrument_base.py b/src/qcodes/instrument/instrument_base.py index 8a1ee2d0cf1..dc747673ad1 100644 --- a/src/qcodes/instrument/instrument_base.py +++ b/src/qcodes/instrument/instrument_base.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any, ClassVar import numpy as np +from typing_extensions import TypedDict from qcodes.logger import get_instrument_logger from qcodes.metadatable import Metadatable, MetadatableWithName @@ -16,6 +17,8 @@ if TYPE_CHECKING: from collections.abc import Callable, Mapping, Sequence + from typing_extensions import NotRequired + from qcodes.instrument.channel import ChannelTuple, InstrumentModule from qcodes.logger.instrument_logger import InstrumentLoggerAdapter @@ -24,6 +27,10 @@ log = logging.getLogger(__name__) +class InstrumentBaseKWArgs(TypedDict): + metadata: NotRequired[Mapping[Any, Any] | None] + label: NotRequired[str | None] + class InstrumentBase(MetadatableWithName, DelegateAttributes): """ Base class for all QCodes instruments and instrument channels diff --git a/src/qcodes/instrument/ip.py b/src/qcodes/instrument/ip.py index a004be14f47..236cd8d7cf4 100644 --- a/src/qcodes/instrument/ip.py +++ b/src/qcodes/instrument/ip.py @@ -5,12 +5,16 @@ import socket from typing import TYPE_CHECKING, Any -from .base import Instrument +from .instrument import Instrument if TYPE_CHECKING: from collections.abc import Sequence from types import TracebackType + from typing_extensions import Unpack + + from .instrument_base import InstrumentBaseKWArgs + log = logging.getLogger(__name__) @@ -21,25 +25,17 @@ class IPInstrument(Instrument): Args: name: What this instrument is called locally. - address: The IP address or name. If not given on construction, must be provided before any communication. - port: The IP port. If not given on construction, must be provided before any communication. - timeout: Seconds to allow for responses. Default 5. - terminator: Character(s) to terminate each send. Default '\n'. - persistent: Whether to leave the socket open between calls. Default True. - write_confirmation: Whether the instrument acknowledges writes with some response we should read. Default True. - - kwargs: additional static metadata to add to this - instrument's JSON snapshot. + **kwargs: Forwarded to the base class. See help for ``qcodes.Instrument`` for additional information on writing instrument subclasses. @@ -54,7 +50,7 @@ def __init__( terminator: str = "\n", persistent: bool = True, write_confirmation: bool = True, - **kwargs: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], ): super().__init__(name, **kwargs) diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index 9ce7ed04d39..bf135e8c57e 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -17,11 +17,13 @@ from qcodes.utils import DelayedKeyboardInterrupt from .instrument import Instrument -from .instrument_base import InstrumentBase +from .instrument_base import InstrumentBase, InstrumentBaseKWArgs if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + VISA_LOGGER = '.'.join((InstrumentBase.__module__, 'com', 'visa')) log = logging.getLogger(__name__) @@ -75,6 +77,7 @@ class VisaInstrument(Instrument): ``qcodes.instruments.sims:AimTTi_PL601P.yaml`` in which case it is loaded from the supplied module. Note that it is an error to pass both ``pyvisa_sim_file`` and ``visalib``. + **kwargs: Other kwargs are forwarded to the baseclass. See help for :class:`.Instrument` for additional information on writing instrument subclasses. @@ -90,7 +93,7 @@ def __init__( device_clear: bool = True, visalib: str | None = None, pyvisa_sim_file: str | None = None, - **kwargs: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], ): super().__init__(name, **kwargs) diff --git a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py index 988ad37dcf4..82df83e07f5 100644 --- a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py +++ b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_sp4t.py @@ -1,8 +1,16 @@ import math -from typing import Optional +from typing import TYPE_CHECKING, Optional from qcodes import validators as vals -from qcodes.instrument import ChannelList, InstrumentChannel, IPInstrument +from qcodes.instrument import ( + ChannelList, + InstrumentBaseKWArgs, + InstrumentChannel, + IPInstrument, +) + +if TYPE_CHECKING: + from typing_extensions import Unpack class MiniCircuitsRCSP4TChannel(InstrumentChannel): @@ -66,10 +74,17 @@ class MiniCircuitsRCSP4T(IPInstrument): name: the name of the instrument address: ip address ie "10.0.0.1" port: port to connect to default Telnet:23 + **kwargs: Forwarded to the baseclass """ - def __init__(self, name: str, address: str, port: int = 23): - super().__init__(name, address, port) + def __init__( + self, + name: str, + address: str, + port: int = 23, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): + super().__init__(name, address, port, **kwargs) self.flush_connection() channels = ChannelList( diff --git a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_spdt.py b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_spdt.py index e3529229189..f0d2c82c8be 100644 --- a/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_spdt.py +++ b/src/qcodes/instrument_drivers/Minicircuits/_minicircuits_rc_spdt.py @@ -1,7 +1,15 @@ -from typing import Optional +from typing import TYPE_CHECKING, Optional from qcodes import validators as vals -from qcodes.instrument import ChannelList, InstrumentChannel, IPInstrument +from qcodes.instrument import ( + ChannelList, + InstrumentBaseKWArgs, + InstrumentChannel, + IPInstrument, +) + +if TYPE_CHECKING: + from typing_extensions import Unpack class MiniCircuitsRCSPDTChannel(InstrumentChannel): @@ -49,8 +57,14 @@ class MiniCircuitsRCSPDT(IPInstrument): port: port to connect to default Telnet:23 """ - def __init__(self, name: str, address: str, port: int = 23): - super().__init__(name, address, port) + def __init__( + self, + name: str, + address: str, + port: int = 23, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): + super().__init__(name, address, port, **kwargs) self.flush_connection() channels = ChannelList( diff --git a/src/qcodes/instrument_drivers/mock_instruments/__init__.py b/src/qcodes/instrument_drivers/mock_instruments/__init__.py index 60721aa907f..c3e100ff36a 100644 --- a/src/qcodes/instrument_drivers/mock_instruments/__init__.py +++ b/src/qcodes/instrument_drivers/mock_instruments/__init__.py @@ -7,7 +7,13 @@ import numpy as np -from qcodes.instrument import ChannelList, Instrument, InstrumentBase, InstrumentChannel +from qcodes.instrument import ( + ChannelList, + Instrument, + InstrumentBase, + InstrumentBaseKWArgs, + InstrumentChannel, +) from qcodes.parameters import ( ArrayParameter, MultiParameter, @@ -21,6 +27,8 @@ if TYPE_CHECKING: from collections.abc import Generator, Sequence + from typing_extensions import Unpack + log = logging.getLogger(__name__) @@ -887,10 +895,15 @@ class SnapShotTestInstrument(DummyBase): a subset of params """ - def __init__(self, name: str, params: Sequence[str] = ('v1', 'v2', 'v3'), - params_to_skip: Sequence[str] = ('v2')): + def __init__( + self, + name: str, + params: Sequence[str] = ("v1", "v2", "v3"), + params_to_skip: Sequence[str] = ("v2"), + **kwargs: Unpack[InstrumentBaseKWArgs], + ): - super().__init__(name) + super().__init__(name, **kwargs) if not(set(params_to_skip).issubset(params)): raise ValueError('Invalid input; params_to_skip must be a subset ' From 3c7c78a0b16d08f07924a714894535fbe6ec326f Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 27 Apr 2024 11:20:41 +0200 Subject: [PATCH 02/23] Add types for visa kwargs --- src/qcodes/instrument/__init__.py | 4 +++- src/qcodes/instrument/visa.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/qcodes/instrument/__init__.py b/src/qcodes/instrument/__init__.py index 3cc1aec4f96..0ae208362f7 100644 --- a/src/qcodes/instrument/__init__.py +++ b/src/qcodes/instrument/__init__.py @@ -19,7 +19,7 @@ from .instrument import Instrument, find_or_create_instrument from .instrument_base import InstrumentBase, InstrumentBaseKWArgs from .ip import IPInstrument -from .visa import VisaInstrument +from .visa import VisaInstrument, VisaInstrumentKWArgs, VisaInstrumentNoTerminatorKWArgs __all__ = [ "ChannelList", @@ -31,5 +31,7 @@ "InstrumentChannel", "InstrumentModule", "VisaInstrument", + "VisaInstrumentNoTerminatorKWArgs", + "VisaInstrumentKWArgs", "find_or_create_instrument", ] diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index bf135e8c57e..9c28c883110 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -47,6 +47,18 @@ def _close_visa_handle( # the resource is already closed pass + +class VisaInstrumentNoTerminatorKWArgs(InstrumentBaseKWArgs): + timeout: float + device_clear: bool + visalib: str | None + pyvisa_sim_file: str | None + + +class VisaInstrumentKWArgs(VisaInstrumentNoTerminatorKWArgs): + terminator: str | None + + class VisaInstrument(Instrument): """ From cd77b7ce88cce6b7104acb9f3d9f0782fa7ccce9 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 27 Apr 2024 11:31:53 +0200 Subject: [PATCH 03/23] Convert some instrument drivers to use typed kwargs --- src/qcodes/instrument_drivers/HP/HP_8133A.py | 15 +++++++++--- src/qcodes/instrument_drivers/HP/HP_83650A.py | 23 +++++++++++------- .../Keithley/Keithley_2400.py | 17 +++++++++---- .../Keithley/Keithley_7510.py | 16 +++++++++++-- .../Keithley/Keithley_s46.py | 21 ++++++++++++---- .../instrument_drivers/QDev/QDac_channels.py | 24 +++++++++++++------ src/qcodes/instrument_drivers/stahl/stahl.py | 19 ++++++++++++--- .../yokogawa/Yokogawa_GS200.py | 17 ++++++++++--- 8 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/qcodes/instrument_drivers/HP/HP_8133A.py b/src/qcodes/instrument_drivers/HP/HP_8133A.py index 40254b3d347..ef6818d7b59 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8133A.py +++ b/src/qcodes/instrument_drivers/HP/HP_8133A.py @@ -1,15 +1,24 @@ -from typing import Any +from typing import TYPE_CHECKING -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs from qcodes.validators import Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + class HP8133A(VisaInstrument): """ QCoDeS driver for Hewlett Packard 8133A Pulse Generator. """ - def __init__(self, name: str, address: str, reset: bool = False, **kwargs: Any): + def __init__( + self, + name: str, + address: str, + reset: bool = False, + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ): super().__init__(name, address, terminator="\n", **kwargs) self.add_parameter( diff --git a/src/qcodes/instrument_drivers/HP/HP_83650A.py b/src/qcodes/instrument_drivers/HP/HP_83650A.py index be5d08f65e8..1a9451480d0 100644 --- a/src/qcodes/instrument_drivers/HP/HP_83650A.py +++ b/src/qcodes/instrument_drivers/HP/HP_83650A.py @@ -2,10 +2,13 @@ # # Written by Bruno Buijtendorp (brunobuijtendorp@gmail.com) import logging -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional from qcodes import validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs + +if TYPE_CHECKING: + from typing_extensions import Unpack log = logging.getLogger(__name__) @@ -20,13 +23,15 @@ class HP83650A(VisaInstrument): """ - def __init__(self, - name: str, - address: str, - verbose: int = 1, - reset: bool = False, - server_name: Optional[str] = None, - **kwargs: Any): + def __init__( + self, + name: str, + address: str, + verbose: int = 1, + reset: bool = False, + server_name: Optional[str] = None, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): self.verbose = verbose log.debug('Initializing instrument') diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py index d09c72d0769..77212369272 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py @@ -1,17 +1,26 @@ -from typing import Any +from typing import TYPE_CHECKING -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs from qcodes.parameters import create_on_off_val_mapping from qcodes.validators import Enum, Strings +if TYPE_CHECKING: + from typing_extensions import Unpack + class Keithley2400(VisaInstrument): """ QCoDeS driver for the Keithley 2400 voltage source. """ - def __init__(self, name: str, address: str, **kwargs: Any): - super().__init__(name, address, terminator="\n", **kwargs) + def __init__( + self, + name: str, + address: str, + terminator="\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ): + super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter( "rangev", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py index 0dd5649a7e6..20c7510ee31 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py @@ -2,7 +2,11 @@ import numpy as np -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + InstrumentChannel, + VisaInstrument, + VisaInstrumentNoTerminatorKWArgs, +) from qcodes.parameters import ( DelegateParameter, MultiParameter, @@ -17,6 +21,8 @@ from collections.abc import Sequence from types import TracebackType + from typing_extensions import Unpack + class DataArray7510(MultiParameter): """ @@ -683,7 +689,13 @@ class Keithley7510(VisaInstrument): The QCoDeS driver for the Keithley 7510 DMM """ - def __init__(self, name: str, address: str, terminator: str = "\n", **kwargs: Any): + def __init__( + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ): """ Create an instance of the instrument. diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py index 8fb4a072e08..e6f355ff499 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py @@ -3,11 +3,18 @@ """ import re from itertools import product -from typing import Any, ClassVar, Optional +from typing import TYPE_CHECKING, Any, ClassVar, Optional -from qcodes.instrument import Instrument, VisaInstrument +from qcodes.instrument import ( + Instrument, + VisaInstrument, + VisaInstrumentNoTerminatorKWArgs, +) from qcodes.parameters import Parameter, ParamRawDataType +if TYPE_CHECKING: + from typing_extensions import Unpack + class KeithleyS46LockAcquisitionError(Exception): pass @@ -136,9 +143,15 @@ class KeithleyS46(VisaInstrument): # Make a reverse dict for efficient alias lookup given a channel number aliases: ClassVar[dict[int, str]] = {v: k for k, v in channel_numbers.items()} - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, + name: str, + address: str, + terminator="\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ): - super().__init__(name, address, terminator="\n", **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) try: self.add_parameter( "closed_channels", diff --git a/src/qcodes/instrument_drivers/QDev/QDac_channels.py b/src/qcodes/instrument_drivers/QDev/QDac_channels.py index 7582d9744da..5d2d082f9cf 100644 --- a/src/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/src/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -10,12 +10,20 @@ from pyvisa.resources.serial import SerialInstrument from qcodes import validators as vals -from qcodes.instrument import ChannelList, Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + ChannelList, + Instrument, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import MultiChannelInstrumentParameter, ParamRawDataType if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + log = logging.getLogger(__name__) @@ -176,12 +184,14 @@ class QDevQDac(VisaInstrument): # set nonzero value (seconds) to accept older status when reading settings max_status_age = 1 - def __init__(self, - name: str, - address: str, - num_chans: int = 48, - update_currents: bool = True, - **kwargs: Any): + def __init__( + self, + name: str, + address: str, + num_chans: int = 48, + update_currents: bool = True, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): """ Instantiates the instrument. diff --git a/src/qcodes/instrument_drivers/stahl/stahl.py b/src/qcodes/instrument_drivers/stahl/stahl.py index 576678e2591..9489a25f9ec 100644 --- a/src/qcodes/instrument_drivers/stahl/stahl.py +++ b/src/qcodes/instrument_drivers/stahl/stahl.py @@ -7,15 +7,23 @@ from collections import OrderedDict from collections.abc import Iterable from functools import partial -from typing import Any, Callable, Optional +from typing import TYPE_CHECKING, Any, Callable, Optional import numpy as np from pyvisa.resources.serial import SerialInstrument from pyvisa.resources.tcpip import TCPIPSocket -from qcodes.instrument import ChannelList, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + ChannelList, + InstrumentChannel, + VisaInstrument, + VisaInstrumentNoTerminatorKWArgs, +) from qcodes.validators import Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + logger = logging.getLogger() @@ -157,7 +165,12 @@ class Stahl(VisaInstrument): In this case the VISA address would be: ``"TCPIP0::hostname::8088::SOCKET"`` """ - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, + name: str, + address: str, + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ): super().__init__(name, address, terminator="\r", **kwargs) if isinstance(self.visa_handle, TCPIPSocket): pass # allow connection to remote serial device diff --git a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py index d6d40549ff9..646e6e04079 100644 --- a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py +++ b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py @@ -1,10 +1,17 @@ from functools import partial -from typing import Any, Literal, Optional, Union +from typing import TYPE_CHECKING, Literal, Optional, Union -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + InstrumentChannel, + VisaInstrument, + VisaInstrumentNoTerminatorKWArgs, +) from qcodes.parameters import DelegateParameter from qcodes.validators import Bool, Enum, Ints, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + ModeType = Literal["CURR", "VOLT"] @@ -265,7 +272,11 @@ class YokogawaGS200(VisaInstrument): """ def __init__( - self, name: str, address: str, terminator: str = "\n", **kwargs: Any + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) From 0128409e402a98b2d78051b409675de790106fb8 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 28 Apr 2024 20:47:05 +0200 Subject: [PATCH 04/23] Port agilent drivers to typed kwargs --- .../agilent/Agilent_E8257D.py | 8 +++++--- .../agilent/Agilent_E8267C.py | 16 ++++++++++++---- .../agilent/_Agilent_344xxA.py | 18 ++++++++++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py index 07a172847ef..9f3924deb3a 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py @@ -1,12 +1,14 @@ import warnings -from typing import Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union import numpy as np import qcodes.validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs from qcodes.parameters import create_on_off_val_mapping +if TYPE_CHECKING: + from typing_extensions import Unpack class AgilentE8257D(VisaInstrument): """ @@ -22,7 +24,7 @@ def __init__( address: str, step_attenuator: Optional[bool] = None, terminator: str = "\n", - **kwargs: Any + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py index a1123cbed45..7c73d231020 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py @@ -1,18 +1,26 @@ -from typing import Any, Union +from typing import TYPE_CHECKING, Any, Union import numpy as np -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs from qcodes.validators import Enum, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack class AgilentE8267C(VisaInstrument): """ This is the QCoDeS driver for the Agilent E8267C signal generator. """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator="\n", **kwargs) + def __init__( + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ) -> None: + super().__init__(name, address, terminator=terminator, **kwargs) # general commands self.add_parameter( name="frequency", diff --git a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py index 9c296243892..5b6fa5c6556 100644 --- a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py +++ b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py @@ -1,9 +1,13 @@ -from typing import Any -from qcodes.instrument import VisaInstrument +from typing import TYPE_CHECKING + +from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs from qcodes.parameters import Parameter from qcodes.validators import Enum, Strings +if TYPE_CHECKING: + from typing_extensions import Unpack + class _Agilent344xxA(VisaInstrument): """ @@ -13,8 +17,14 @@ class _Agilent344xxA(VisaInstrument): Note that most models are better supported by the Keysight 33xxA drivers. """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator="\n", **kwargs) + def __init__( + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + ) -> None: + super().__init__(name, address, terminator=terminator, **kwargs) idn = self.IDN.get() self.model = idn["model"] From f3ffd500c359a165b4b62ac6e30c82f229e4ddc1 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sun, 28 Apr 2024 20:52:06 +0200 Subject: [PATCH 05/23] Fix some type checking issues --- src/qcodes/instrument_drivers/HP/HP_8133A.py | 3 ++- src/qcodes/instrument_drivers/Keithley/Keithley_2400.py | 2 +- src/qcodes/instrument_drivers/Keithley/Keithley_s46.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qcodes/instrument_drivers/HP/HP_8133A.py b/src/qcodes/instrument_drivers/HP/HP_8133A.py index ef6818d7b59..5269ba7e849 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8133A.py +++ b/src/qcodes/instrument_drivers/HP/HP_8133A.py @@ -17,9 +17,10 @@ def __init__( name: str, address: str, reset: bool = False, + terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", ): - super().__init__(name, address, terminator="\n", **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter( name="frequency", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py index 77212369272..d385ac5763b 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py @@ -17,7 +17,7 @@ def __init__( self, name: str, address: str, - terminator="\n", + terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", ): super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py index e6f355ff499..886ad369f1f 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py @@ -147,7 +147,7 @@ def __init__( self, name: str, address: str, - terminator="\n", + terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", ): From e194024ad551963bb38d05e4daa66befe9cefee3 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 09:54:11 +0200 Subject: [PATCH 06/23] remove special case for visa kwargs --- src/qcodes/instrument/__init__.py | 3 +-- src/qcodes/instrument/visa.py | 6 +----- src/qcodes/instrument_drivers/HP/HP_8133A.py | 4 ++-- src/qcodes/instrument_drivers/HP/HP_83650A.py | 3 ++- src/qcodes/instrument_drivers/Keithley/Keithley_2400.py | 4 ++-- src/qcodes/instrument_drivers/Keithley/Keithley_7510.py | 4 ++-- src/qcodes/instrument_drivers/Keithley/Keithley_s46.py | 4 ++-- src/qcodes/instrument_drivers/QDev/QDac_channels.py | 6 +++++- src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py | 4 ++-- src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py | 4 ++-- src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py | 4 ++-- src/qcodes/instrument_drivers/stahl/stahl.py | 7 ++++--- src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py | 4 ++-- 13 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/qcodes/instrument/__init__.py b/src/qcodes/instrument/__init__.py index 0ae208362f7..5713b58b592 100644 --- a/src/qcodes/instrument/__init__.py +++ b/src/qcodes/instrument/__init__.py @@ -19,7 +19,7 @@ from .instrument import Instrument, find_or_create_instrument from .instrument_base import InstrumentBase, InstrumentBaseKWArgs from .ip import IPInstrument -from .visa import VisaInstrument, VisaInstrumentKWArgs, VisaInstrumentNoTerminatorKWArgs +from .visa import VisaInstrument, VisaInstrumentKWArgs __all__ = [ "ChannelList", @@ -31,7 +31,6 @@ "InstrumentChannel", "InstrumentModule", "VisaInstrument", - "VisaInstrumentNoTerminatorKWArgs", "VisaInstrumentKWArgs", "find_or_create_instrument", ] diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index 9c28c883110..23ff3acd60c 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -48,17 +48,13 @@ def _close_visa_handle( pass -class VisaInstrumentNoTerminatorKWArgs(InstrumentBaseKWArgs): +class VisaInstrumentKWArgs(InstrumentBaseKWArgs): timeout: float device_clear: bool visalib: str | None pyvisa_sim_file: str | None -class VisaInstrumentKWArgs(VisaInstrumentNoTerminatorKWArgs): - terminator: str | None - - class VisaInstrument(Instrument): """ diff --git a/src/qcodes/instrument_drivers/HP/HP_8133A.py b/src/qcodes/instrument_drivers/HP/HP_8133A.py index 5269ba7e849..b3b868262ca 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8133A.py +++ b/src/qcodes/instrument_drivers/HP/HP_8133A.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.validators import Numbers if TYPE_CHECKING: @@ -18,7 +18,7 @@ def __init__( address: str, reset: bool = False, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/HP/HP_83650A.py b/src/qcodes/instrument_drivers/HP/HP_83650A.py index 1a9451480d0..8edd9496b69 100644 --- a/src/qcodes/instrument_drivers/HP/HP_83650A.py +++ b/src/qcodes/instrument_drivers/HP/HP_83650A.py @@ -30,12 +30,13 @@ def __init__( verbose: int = 1, reset: bool = False, server_name: Optional[str] = None, + terminator: Optional[str] = None, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): self.verbose = verbose log.debug('Initializing instrument') - super().__init__(name, address, **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter('frequency', label='Frequency', diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py index d385ac5763b..0ef9084d34e 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import create_on_off_val_mapping from qcodes.validators import Enum, Strings @@ -18,7 +18,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py index 20c7510ee31..03801dda100 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py @@ -5,7 +5,7 @@ from qcodes.instrument import ( InstrumentChannel, VisaInstrument, - VisaInstrumentNoTerminatorKWArgs, + VisaInstrumentKWArgs, ) from qcodes.parameters import ( DelegateParameter, @@ -694,7 +694,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ Create an instance of the instrument. diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py index 886ad369f1f..ebfa4b77d8a 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py @@ -8,7 +8,7 @@ from qcodes.instrument import ( Instrument, VisaInstrument, - VisaInstrumentNoTerminatorKWArgs, + VisaInstrumentKWArgs, ) from qcodes.parameters import Parameter, ParamRawDataType @@ -148,7 +148,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/QDev/QDac_channels.py b/src/qcodes/instrument_drivers/QDev/QDac_channels.py index 5d2d082f9cf..d27a2db93e6 100644 --- a/src/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/src/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -190,6 +190,7 @@ def __init__( address: str, num_chans: int = 48, update_currents: bool = True, + terminator: Optional[str] = None, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ @@ -201,12 +202,15 @@ def __init__( num_chans: Number of channels to assign. Default: 48 update_currents: Whether to query all channels for their current current value on startup. Default: True. + terminator: Read and write termination character(s). + If None the terminator will not be set and we + rely on the defaults from PyVisa. Default None. **kwargs: kwargs are forwarded to base class. Returns: QDac object """ - super().__init__(name, address, **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) self._output_n_lines = 50 handle = self.visa_handle assert isinstance(handle, SerialInstrument) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py index 9f3924deb3a..b0001edcd6a 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py @@ -4,7 +4,7 @@ import numpy as np import qcodes.validators as vals -from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import create_on_off_val_mapping if TYPE_CHECKING: @@ -24,7 +24,7 @@ def __init__( address: str, step_attenuator: Optional[bool] = None, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py index 7c73d231020..dc0995f7147 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py @@ -2,7 +2,7 @@ import numpy as np -from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.validators import Enum, Numbers if TYPE_CHECKING: @@ -18,7 +18,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) # general commands diff --git a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py index 5b6fa5c6556..6ad5412d704 100644 --- a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py +++ b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING -from qcodes.instrument import VisaInstrument, VisaInstrumentNoTerminatorKWArgs +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import Parameter from qcodes.validators import Enum, Strings @@ -22,7 +22,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/stahl/stahl.py b/src/qcodes/instrument_drivers/stahl/stahl.py index 9489a25f9ec..64d28760ade 100644 --- a/src/qcodes/instrument_drivers/stahl/stahl.py +++ b/src/qcodes/instrument_drivers/stahl/stahl.py @@ -17,7 +17,7 @@ ChannelList, InstrumentChannel, VisaInstrument, - VisaInstrumentNoTerminatorKWArgs, + VisaInstrumentKWArgs, ) from qcodes.validators import Numbers @@ -169,9 +169,10 @@ def __init__( self, name: str, address: str, - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + terminator: Optional[str] = "\r", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): - super().__init__(name, address, terminator="\r", **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) if isinstance(self.visa_handle, TCPIPSocket): pass # allow connection to remote serial device elif isinstance(self.visa_handle, SerialInstrument): diff --git a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py index 646e6e04079..4d9d1f2d963 100644 --- a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py +++ b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py @@ -4,7 +4,7 @@ from qcodes.instrument import ( InstrumentChannel, VisaInstrument, - VisaInstrumentNoTerminatorKWArgs, + VisaInstrumentKWArgs, ) from qcodes.parameters import DelegateParameter from qcodes.validators import Bool, Enum, Ints, Numbers @@ -276,7 +276,7 @@ def __init__( name: str, address: str, terminator: str = "\n", - **kwargs: "Unpack[VisaInstrumentNoTerminatorKWArgs]", + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) From 650d45718542c45af1086fd15f31a79859f4d3e1 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 15:21:30 +0200 Subject: [PATCH 07/23] mark as not required --- src/qcodes/instrument/visa.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index 23ff3acd60c..b1026fc58b8 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -22,7 +22,7 @@ if TYPE_CHECKING: from collections.abc import Sequence - from typing_extensions import Unpack + from typing_extensions import NotRequired, Unpack VISA_LOGGER = '.'.join((InstrumentBase.__module__, 'com', 'visa')) @@ -49,10 +49,10 @@ def _close_visa_handle( class VisaInstrumentKWArgs(InstrumentBaseKWArgs): - timeout: float - device_clear: bool - visalib: str | None - pyvisa_sim_file: str | None + timeout: NotRequired[float] + device_clear: NotRequired[bool] + visalib: NotRequired[str | None] + pyvisa_sim_file: NotRequired[str | None] class VisaInstrument(Instrument): From e5a24693d820e3ceb51e61fd44df76b490b4f433 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 15:24:28 +0200 Subject: [PATCH 08/23] work around docs build error --- src/qcodes/instrument/visa.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index b1026fc58b8..3a67bc2041e 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -4,7 +4,7 @@ import logging import warnings from importlib.resources import as_file, files -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, TypedDict from weakref import finalize import pyvisa @@ -20,7 +20,7 @@ from .instrument_base import InstrumentBase, InstrumentBaseKWArgs if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Mapping, Sequence from typing_extensions import NotRequired, Unpack @@ -48,7 +48,9 @@ def _close_visa_handle( pass -class VisaInstrumentKWArgs(InstrumentBaseKWArgs): +class VisaInstrumentKWArgs(TypedDict): + metadata: NotRequired[Mapping[Any, Any] | None] + label: NotRequired[str | None] timeout: NotRequired[float] device_clear: NotRequired[bool] visalib: NotRequired[str | None] From 73eb30930ddbc92d376f22afd71a45f67e728b7a Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 15:28:32 +0200 Subject: [PATCH 09/23] Document Visa Args --- src/qcodes/instrument/instrument_base.py | 14 ++++++++++ src/qcodes/instrument/visa.py | 35 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/qcodes/instrument/instrument_base.py b/src/qcodes/instrument/instrument_base.py index dc747673ad1..050503f0769 100644 --- a/src/qcodes/instrument/instrument_base.py +++ b/src/qcodes/instrument/instrument_base.py @@ -28,8 +28,22 @@ class InstrumentBaseKWArgs(TypedDict): + """ + This TypedDict defines the type of the kwargs that can be passed to the InstrumentBase class. + A subclass of VisaInstrument should take ``**kwargs: Unpack[InstrumentBaseKWArgs]`` as input + and forward this to the super class to ensure that it can accept all the arguments defined here. + """ + metadata: NotRequired[Mapping[Any, Any] | None] + """ + Additional static metadata to add to this + instrument's JSON snapshot. + """ label: NotRequired[str | None] + """ + Nicely formatted name of the instrument; if None, + the ``name`` is used. + """ class InstrumentBase(MetadatableWithName, DelegateAttributes): """ diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index 3a67bc2041e..893edfbfacc 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -49,12 +49,47 @@ def _close_visa_handle( class VisaInstrumentKWArgs(TypedDict): + """ + This TypedDict defines the type of the kwargs that can be passed to the VisaInstrument class. + A subclass of VisaInstrument should take ``**kwargs: Unpack[VisaInstrumentKWArgs]`` as input + and forward this to the super class to ensure that it can accept all the arguments defined here. + """ + metadata: NotRequired[Mapping[Any, Any] | None] + """ + Additional static metadata to add to this + instrument's JSON snapshot. + """ label: NotRequired[str | None] + """ + Nicely formatted name of the instrument; if None, + the ``name`` is used. + """ timeout: NotRequired[float] + "Seconds to allow for responses. Default 5." device_clear: NotRequired[bool] + "Perform a device clear. Default True." visalib: NotRequired[str | None] + """ + Visa backend to use when connecting to this instrument. + This should be in the form of a string '@'. + Both parts can be omitted and pyvisa will try to infer the + path to the visa backend file. + By default the IVI backend is used if found, but '@py' will use the + ``pyvisa-py`` backend. Note that QCoDeS does not install (or even require) + ANY backends, it is up to the user to do that. see eg: + http://pyvisa.readthedocs.org/en/stable/names.html + """ pyvisa_sim_file: NotRequired[str | None] + """ + Name of a pyvisa-sim yaml file used to simulate the instrument. + The file is expected to be loaded from a python module. + The file can be given either as only the file name in which case it is loaded + from ``qcodes.instruments.sims`` or in the format ``module:filename`` e.g. + ``qcodes.instruments.sims:AimTTi_PL601P.yaml`` in which case it is loaded + from the supplied module. Note that it is an error to pass both + ``pyvisa_sim_file`` and ``visalib``. + """ class VisaInstrument(Instrument): From 5cd0e4c2f8da9dc63eb1ac407e4fe38315f02f3c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 16:01:56 +0200 Subject: [PATCH 10/23] Port AimTTi drivers to explicit kwargs --- .../instrument_drivers/AimTTi/_AimTTi_PL_P.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py b/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py index cc83721db56..e19e4ed5598 100644 --- a/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py +++ b/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py @@ -1,9 +1,18 @@ -from typing import Any, ClassVar, Optional +from typing import TYPE_CHECKING, Any, ClassVar, Optional from qcodes import validators as vals -from qcodes.instrument import ChannelList, Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + ChannelList, + Instrument, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import create_on_off_val_mapping +if TYPE_CHECKING: + from typing_extensions import Unpack + class NotKnownModel(Exception): """ @@ -225,14 +234,23 @@ class AimTTi(VisaInstrument): "QL355TP": 3, } - def __init__(self, name: str, address: str, **kwargs: Any) -> None: + def __init__( + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ) -> None: """ Args: name: Name to use internally in QCoDeS. address: VISA resource address + terminator: Read and write termination character(s). + If None the terminator will not be set and we + rely on the defaults from PyVisa. Default None. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator="\n", **kwargs) + super().__init__(name, address, terminator=terminator, **kwargs) channels = ChannelList(self, "Channels", AimTTiChannel, snapshotable=False) From 9cf336018eb0ed2b8d762aa8580b8d1822cd5280 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 29 Apr 2024 16:53:49 +0200 Subject: [PATCH 11/23] port ami driver to explicit kwargs --- .../american_magnetics/AMI430_visa.py | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py index abaec1484aa..7372c33bb44 100644 --- a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py +++ b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py @@ -8,17 +8,26 @@ from collections.abc import Iterable, Sequence from contextlib import ExitStack from functools import partial -from typing import Any, Callable, ClassVar, TypeVar, cast +from typing import TYPE_CHECKING, Any, Callable, ClassVar, TypeVar, cast import numpy as np from pyvisa import VisaIOError -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.math_utils import FieldVector from qcodes.parameters import Parameter from qcodes.utils import QCoDeSDeprecationWarning from qcodes.validators import Anything, Bool, Enum, Ints, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + + log = logging.getLogger(__name__) CartesianFieldLimitFunction = Callable[[float, float, float], bool] @@ -132,21 +141,7 @@ def check_state(self) -> bool: class AMIModel430(VisaInstrument): - """ - Driver for the American Magnetics Model 430 magnet power supply programmer. - - This class controls a single magnet power supply. In order to use two or - three magnets simultaneously to set field vectors, first instantiate the - individual magnets using this class and then pass them as arguments to - the AMIModel4303D virtual instrument classes. - - Args: - name: a name for the instrument - address: VISA formatted address of the power supply programmer. - Of the form ``TCPIP[board]::host address::port::SOCKET`` - e.g. ``TCPIP0::192.168.0.1::7800::SOCKET`` - current_ramp_limit: A current ramp limit, in units of A/s - """ + _SHORT_UNITS: ClassVar[dict[str, str]] = { "seconds": "s", @@ -165,15 +160,34 @@ def __init__( reset: bool = False, terminator: str = "\r\n", current_ramp_limit: float | None = None, - **kwargs: Any, + **kwargs: Unpack[VisaInstrumentKWArgs], ): + """ + Driver for the American Magnetics Model 430 magnet power supply programmer. + + This class controls a single magnet power supply. In order to use two or + three magnets simultaneously to set field vectors, first instantiate the + individual magnets using this class and then pass them as arguments to + the AMIModel4303D virtual instrument classes. + + Args: + name: a name for the instrument + address: VISA formatted address of the power supply programmer. + Of the form ``TCPIP[board]::host address::port::SOCKET`` + e.g. ``TCPIP0::192.168.0.1::7800::SOCKET`` + reset: Should the reset method be called on the instrument at init time + terminator: Read and write termination character(s). + current_ramp_limit: A current ramp limit, in units of A/s + **kwargs: Additional kwargs are passed to the base class + """ if "has_current_rating" in kwargs.keys(): warnings.warn( "'has_current_rating' kwarg to AMIModel430 " "is deprecated and has no effect", category=QCoDeSDeprecationWarning, ) - kwargs.pop("has_current_rating") + # this key should not be here so mypy complains about it + kwargs.pop("has_current_rating") # type: ignore super().__init__( name, From 64c6449b91544f5bb9209c8ac4b83d0674e69986 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 30 Apr 2024 09:16:54 +0200 Subject: [PATCH 12/23] fix missing type category --- src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py index 7372c33bb44..9709a734023 100644 --- a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py +++ b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py @@ -187,7 +187,7 @@ def __init__( category=QCoDeSDeprecationWarning, ) # this key should not be here so mypy complains about it - kwargs.pop("has_current_rating") # type: ignore + kwargs.pop("has_current_rating") # type: ignore[typeddict-item] super().__init__( name, From 60c672cdf9f89e0c535dca8b64dd1297fae73208 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 30 Apr 2024 09:29:08 +0200 Subject: [PATCH 13/23] Convert instruments upto and including ithaco --- .../instrument_drivers/Galil/dmc_41x3.py | 12 +++++++--- src/qcodes/instrument_drivers/HP/HP_8753D.py | 17 +++++++++---- .../instrument_drivers/Harvard/Decadac.py | 24 +++++++++++++------ .../instrument_drivers/basel/BaselSP983.py | 13 +++++++--- .../instrument_drivers/basel/BaselSP983a.py | 12 ++++++---- .../instrument_drivers/ithaco/Ithaco_1211.py | 10 +++++--- 6 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/qcodes/instrument_drivers/Galil/dmc_41x3.py b/src/qcodes/instrument_drivers/Galil/dmc_41x3.py index d4d058654ff..b1a4046f7be 100644 --- a/src/qcodes/instrument_drivers/Galil/dmc_41x3.py +++ b/src/qcodes/instrument_drivers/Galil/dmc_41x3.py @@ -3,14 +3,18 @@ Colloquially known as the "stepper motors". """ -from typing import Any, Optional + +from typing import TYPE_CHECKING, Any, Optional import numpy as np import numpy.typing as npt -from qcodes.instrument import Instrument, InstrumentChannel +from qcodes.instrument import Instrument, InstrumentBaseKWArgs, InstrumentChannel from qcodes.validators import Enum, Ints, Multiples +if TYPE_CHECKING: + from typing_extensions import Unpack + try: import gclib # pyright: ignore[reportMissingImports] except ImportError as e: @@ -29,7 +33,9 @@ class GalilMotionController(Instrument): Base class for Galil Motion Controller drivers """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: + def __init__( + self, name: str, address: str, **kwargs: "Unpack[InstrumentBaseKWArgs]" + ) -> None: """ Initializes and opens the connection to the Galil Motion Controller diff --git a/src/qcodes/instrument_drivers/HP/HP_8753D.py b/src/qcodes/instrument_drivers/HP/HP_8753D.py index c064fbd14b1..7ccfaa25cae 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8753D.py +++ b/src/qcodes/instrument_drivers/HP/HP_8753D.py @@ -1,13 +1,16 @@ import logging from functools import partial -from typing import Any, Union +from typing import TYPE_CHECKING, Union import numpy as np import qcodes.validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import ArrayParameter, ParamRawDataType +if TYPE_CHECKING: + from typing_extensions import Unpack + log = logging.getLogger(__name__) _unit_map = { @@ -112,8 +115,14 @@ class HP8753D(VisaInstrument): QCoDeS driver for the Hewlett Packard 8753D Network Analyzer. """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator="\n", **kwargs) + def __init__( + self, + name: str, + address: str, + terminator: str = "\n", + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ) -> None: + super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter( "start_freq", diff --git a/src/qcodes/instrument_drivers/Harvard/Decadac.py b/src/qcodes/instrument_drivers/Harvard/Decadac.py index 67328c7bde6..63dec51283e 100644 --- a/src/qcodes/instrument_drivers/Harvard/Decadac.py +++ b/src/qcodes/instrument_drivers/Harvard/Decadac.py @@ -1,12 +1,17 @@ from functools import partial from time import time -from typing import Union, cast +from typing import TYPE_CHECKING, cast import qcodes.validators as vals -from qcodes.instrument import ChannelList, InstrumentChannel, VisaInstrument - -number = Union[float, int] +from qcodes.instrument import ( + ChannelList, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) +if TYPE_CHECKING: + from typing_extensions import Unpack class HarvardDecadacException(Exception): pass @@ -441,9 +446,14 @@ class HarvardDecadac(VisaInstrument, DacReader): DAC_CHANNEL_CLASS = HarvardDecadacChannel DAC_SLOT_CLASS = HarvardDecadacSlot - def __init__(self, name: str, address: str, - min_val: number=-5, max_val: number=5, - **kwargs) -> None: + def __init__( + self, + name: str, + address: str, + min_val: float = -5, + max_val: float = 5, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ) -> None: """ Creates an instance of the Decadac instruments diff --git a/src/qcodes/instrument_drivers/basel/BaselSP983.py b/src/qcodes/instrument_drivers/basel/BaselSP983.py index 4dd0a08c2fc..c7ed4684a3b 100644 --- a/src/qcodes/instrument_drivers/basel/BaselSP983.py +++ b/src/qcodes/instrument_drivers/basel/BaselSP983.py @@ -1,9 +1,12 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import Instrument +from qcodes.instrument import Instrument, InstrumentBaseKWArgs from qcodes.parameters import DelegateParameter, Parameter from qcodes.validators import Enum, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + class BaselSP983(Instrument): """ @@ -29,10 +32,14 @@ class BaselSP983(Instrument): used to set offset voltage parameter of the preamp and the source parameter should represent a voltage source that is connected to the "Offset Input Voltage" connector of the SP983C. + **kwargs: Forwarded to base class. """ def __init__( - self, name: str, input_offset_voltage: Optional[Parameter] = None, **kwargs: Any + self, + name: str, + input_offset_voltage: Optional[Parameter] = None, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ): super().__init__(name, **kwargs) diff --git a/src/qcodes/instrument_drivers/basel/BaselSP983a.py b/src/qcodes/instrument_drivers/basel/BaselSP983a.py index ae76d5212f6..d8c6b291f9f 100644 --- a/src/qcodes/instrument_drivers/basel/BaselSP983a.py +++ b/src/qcodes/instrument_drivers/basel/BaselSP983a.py @@ -1,11 +1,14 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional import numpy as np from qcodes import validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import DelegateParameter, Parameter +if TYPE_CHECKING: + from typing_extensions import Unpack + class BaselSP983a(VisaInstrument): """ @@ -19,7 +22,8 @@ class BaselSP983a(VisaInstrument): user's responsibility to ensure this. This source parameter is used to set offset voltage parameter of the preamp and the source parameter should represent a voltage source that is - connected to the "Offset Input Volgate" connector of the SP983C. + connected to the "Offset Input Voltage" connector of the SP983C. + **kwargs: Forwarded to base class. """ def __init__( @@ -28,7 +32,7 @@ def __init__( address: str, input_offset_voltage: Optional[Parameter] = None, terminator: str = "\r\n", - **kwargs: Any, + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: super().__init__(name, address, terminator=terminator, **kwargs) diff --git a/src/qcodes/instrument_drivers/ithaco/Ithaco_1211.py b/src/qcodes/instrument_drivers/ithaco/Ithaco_1211.py index e59b6a3f24a..6c2ff341cfd 100644 --- a/src/qcodes/instrument_drivers/ithaco/Ithaco_1211.py +++ b/src/qcodes/instrument_drivers/ithaco/Ithaco_1211.py @@ -1,9 +1,12 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import Instrument +from qcodes.instrument import Instrument, InstrumentBaseKWArgs from qcodes.parameters import MultiParameter, Parameter, ParamRawDataType from qcodes.validators import Bool, Enum +if TYPE_CHECKING: + from typing_extensions import Unpack + class CurrentParameter(MultiParameter): """ @@ -69,7 +72,8 @@ class Ithaco1211(Instrument): This is a virtual driver only and will not talk to your instrument. """ - def __init__(self, name: str, **kwargs: Any): + + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]"): super().__init__(name, **kwargs) self.add_parameter('sens', From 9b1cbe3e99415ddb011309b6a9b77269ce4aaf0a Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 30 Apr 2024 07:37:59 +0200 Subject: [PATCH 14/23] add types to InstrumentModule --- src/qcodes/instrument/channel.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qcodes/instrument/channel.py b/src/qcodes/instrument/channel.py index 6a1b8a2bc51..9e658b7b981 100644 --- a/src/qcodes/instrument/channel.py +++ b/src/qcodes/instrument/channel.py @@ -19,7 +19,10 @@ from .instrument_base import InstrumentBase if TYPE_CHECKING: + from typing_extensions import Unpack + from .instrument import Instrument + from .instrument_base import InstrumentBaseKWArgs class InstrumentModule(InstrumentBase): @@ -33,15 +36,14 @@ class InstrumentModule(InstrumentBase): Args: parent: The instrument to which this module should be attached. - name: The name of this module. + **kwargs: Forwarded to the base class. """ - def __init__(self, - parent: InstrumentBase, - name: str, - **kwargs: Any) -> None: + def __init__( + self, parent: InstrumentBase, name: str, **kwargs: Unpack[InstrumentBaseKWArgs] + ) -> None: # need to specify parent before `super().__init__` so that the right # `full_name` is available in that scope. `full_name` is used for # registering the filter for the log messages. It is composed by From 92a70fff828f066050e463fa6581573ac019d6b7 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 30 Apr 2024 16:03:20 +0200 Subject: [PATCH 15/23] update driver example notebooks with typed kwargs --- ...etpoints-Example-with-Dual-Setpoints.ipynb | 28 ++++---------- .../Creating-Instrument-Drivers.ipynb | 38 +++++++++++++------ ...reating-Simulated-PyVISA-Instruments.ipynb | 11 ++++-- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/examples/writing_drivers/A-ParameterWithSetpoints-Example-with-Dual-Setpoints.ipynb b/docs/examples/writing_drivers/A-ParameterWithSetpoints-Example-with-Dual-Setpoints.ipynb index d224d2f4806..02b6744bd32 100644 --- a/docs/examples/writing_drivers/A-ParameterWithSetpoints-Example-with-Dual-Setpoints.ipynb +++ b/docs/examples/writing_drivers/A-ParameterWithSetpoints-Example-with-Dual-Setpoints.ipynb @@ -15,24 +15,12 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Logging hadn't been started.\n", - "Activating auto-logging. Current session state plus future input saved.\n", - "Filename : C:\\Users\\jenielse\\.qcodes\\logs\\command_history.log\n", - "Mode : append\n", - "Output logging : True\n", - "Raw input log : False\n", - "Timestamping : True\n", - "State : active\n", - "Qcodes Logfile : C:\\Users\\jenielse\\.qcodes\\logs\\220617-25816-qcodes.log\n" - ] - } - ], + "outputs": [], "source": [ + "from typing import TYPE_CHECKING\n", + "\n", + "if TYPE_CHECKING:\n", + " from typing_extensions import Unpack\n", "import os\n", "\n", "import numpy as np\n", @@ -44,7 +32,7 @@ " load_or_create_experiment,\n", " plot_dataset,\n", ")\n", - "from qcodes.instrument import Instrument\n", + "from qcodes.instrument import Instrument, InstrumentBaseKWArgs\n", "from qcodes.parameters import Parameter, ParameterWithSetpoints" ] }, @@ -111,7 +99,7 @@ "\n", "class OzzyLowScope(Instrument):\n", "\n", - " def __init__(self, name, **kwargs):\n", + " def __init__(self, name: str, **kwargs: \"Unpack[InstrumentBaseKWArgs]\"):\n", "\n", " super().__init__(name, **kwargs)\n", "\n", @@ -494,7 +482,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.12.1" }, "toc": { "base_numbering": 1, diff --git a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb index a4dae1c6886..25d304f4415 100644 --- a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb +++ b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb @@ -18,12 +18,23 @@ "import ctypes # only for DLL-based instrument\n", "import sys\n", "import time\n", - "from typing import Any\n", + "from typing import TYPE_CHECKING, Any\n", + "\n", + "if TYPE_CHECKING:\n", + " from typing_extensions import (\n", + " Unpack, # can be imported from typing if python >= 3.12\n", + " )\n", "\n", "import numpy as np\n", "\n", "from qcodes import validators as vals\n", - "from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument\n", + "from qcodes.instrument import (\n", + " Instrument,\n", + " InstrumentBaseKWArgs,\n", + " InstrumentChannel,\n", + " VisaInstrument,\n", + " VisaInstrumentKWArgs,\n", + ")\n", "from qcodes.instrument_drivers.AlazarTech.utils import TraceParameter\n", "from qcodes.parameters import ManualParameter, MultiParameter, Parameter" ] @@ -196,10 +207,13 @@ " \"\"\"\n", "\n", " # all instrument constructors should accept **kwargs and pass them on to\n", - " # super().__init__\n", - " def __init__(self, name, address, **kwargs):\n", + " # super().__init__ By using Unpack[VisaKWArgs] we ensure that the instrument class takes exactly the same keyword args as the base class\n", + " # Visa instruments are also required to take name, address and terminator as arguments.\n", + " # name and address should be positional arguments and terminator a keyword argument with the default value set to the terminator that the\n", + " # instrument expects (check the instrument manual)\n", + " def __init__(self, name: str, address: str, terminator: str = \"\\r\", **kwargs: \"Unpack[VisaInstrumentKWArgs]\"):\n", " # supplying the terminator means you don't need to remove it from every response\n", - " super().__init__(name, address, terminator=\"\\r\", **kwargs)\n", + " super().__init__(name, address, terminator=terminator, **kwargs)\n", "\n", " self.attenuation = Parameter(\n", " \"attenuation\",\n", @@ -266,7 +280,7 @@ " SMUA and SMUB.\n", " \"\"\"\n", "\n", - " def __init__(self, parent: Instrument, name: str, channel: str) -> None:\n", + " def __init__(self, parent: Instrument, name: str, channel: str, **kwargs: \"Unpack[InstrumentBaseKWArgs]\") -> None:\n", " \"\"\"\n", " Args:\n", " parent: The Instrument instance to which the channel is\n", @@ -274,12 +288,13 @@ " name: The 'colloquial' name of the channel\n", " channel: The name used by the Keithley, i.e. either\n", " 'smua' or 'smub'\n", + " **kwargs: Forwarded to base class.\n", " \"\"\"\n", "\n", " if channel not in [\"smua\", \"smub\"]:\n", " raise ValueError('channel must be either \"smub\" or \"smua\"')\n", "\n", - " super().__init__(parent, name)\n", + " super().__init__(parent, name, **kwargs)\n", " self.model = self._parent.model\n", "\n", " self.volt = Parameter(\n", @@ -343,11 +358,12 @@ " tested with Keithley2614B\n", " \"\"\"\n", "\n", - " def __init__(self, name: str, address: str, **kwargs) -> None:\n", + " def __init__(self, name: str, address: str, terminator=\"\\n\", **kwargs: \"Unpack[VisaInstrumentKWArgs]\") -> None:\n", " \"\"\"\n", " Args:\n", " name: Name to use internally in QCoDeS\n", " address: VISA ressource address\n", + " terminator: read/write terminator for communication\n", " **kwargs: kwargs are forwarded to the base class.\n", " \"\"\"\n", " super().__init__(name, address, terminator=\"\\n\", **kwargs)\n", @@ -416,7 +432,7 @@ "class AlazarTechATS(Instrument):\n", " dll_path = \"C:\\\\WINDOWS\\\\System32\\\\ATSApi\"\n", "\n", - " def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs):\n", + " def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs: \"Unpack[InstrumentBaseKWArgs]\"):\n", " super().__init__(name, **kwargs)\n", "\n", " # connect to the DLL\n", @@ -471,7 +487,7 @@ "class SomeDLLInstrument(Instrument):\n", " dll_path = \"C:\\\\WINDOWS\\\\System32\\\\ATSApi\"\n", "\n", - " def __init__(self, name, dll_path=None, **kwargs):\n", + " def __init__(self, name, dll_path=None, **kwargs: \"Unpack[InstrumentBaseKWArgs]\"):\n", " super().__init__(name, **kwargs)\n", "\n", " if sys.platform != \"win32\":\n", @@ -557,7 +573,7 @@ " This is a virtual driver only and will not talk to your instrument.\n", " \"\"\"\n", "\n", - " def __init__(self, name, **kwargs):\n", + " def __init__(self, name: str, **kwargs: \"Unpack[InstrumentBaseKWArgs]\"):\n", " super().__init__(name, **kwargs)\n", "\n", " # ManualParameter has an \"initial_value\" kwarg, but if you use this\n", diff --git a/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb b/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb index 73432e62bd9..b8481806c5a 100644 --- a/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb +++ b/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb @@ -66,10 +66,15 @@ } ], "source": [ + "from typing import TYPE_CHECKING\n", + "\n", "import numpy as np\n", "\n", "import qcodes.validators as vals\n", - "from qcodes.instrument.visa import VisaInstrument\n", + "from qcodes.instrument.visa import VisaInstrument, VisaInstrumentKWArgs\n", + "\n", + "if TYPE_CHECKING:\n", + " from typing_extensions import Unpack\n", "\n", "\n", "class Weinschel8320(VisaInstrument):\n", @@ -78,8 +83,8 @@ " Weinschel is formerly known as Aeroflex/Weinschel\n", " \"\"\"\n", "\n", - " def __init__(self, name, address, **kwargs):\n", - " super().__init__(name, address, terminator='\\r', **kwargs)\n", + " def __init__(self, name: str, address: str, terminator='\\r', **kwargs: 'Unpack[VisaInstrumentKWArgs]'):\n", + " super().__init__(name, address, terminator=terminator, **kwargs)\n", "\n", " self.add_parameter('attenuation', unit='dB',\n", " set_cmd='ATTN ALL {:02.0f}',\n", From a0e8866fba80ee2e8ab9072e2ef83481a0b236b2 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 13 May 2024 15:40:46 +0200 Subject: [PATCH 16/23] Alternative way to change terminator and timeout in subclass --- src/qcodes/instrument/visa.py | 49 +++++++++++-------- .../agilent/Agilent_E8257D.py | 5 +- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/qcodes/instrument/visa.py b/src/qcodes/instrument/visa.py index 893edfbfacc..8455a0888ae 100644 --- a/src/qcodes/instrument/visa.py +++ b/src/qcodes/instrument/visa.py @@ -4,7 +4,7 @@ import logging import warnings from importlib.resources import as_file, files -from typing import TYPE_CHECKING, Any, TypedDict +from typing import TYPE_CHECKING, Any, Literal, TypedDict from weakref import finalize import pyvisa @@ -53,42 +53,32 @@ class VisaInstrumentKWArgs(TypedDict): This TypedDict defines the type of the kwargs that can be passed to the VisaInstrument class. A subclass of VisaInstrument should take ``**kwargs: Unpack[VisaInstrumentKWArgs]`` as input and forward this to the super class to ensure that it can accept all the arguments defined here. + + Consult the documentation of :class:`.VisaInstrument` for more information on the arguments. """ metadata: NotRequired[Mapping[Any, Any] | None] """ - Additional static metadata to add to this - instrument's JSON snapshot. + Additional static metadata to add to this instrument's JSON snapshot. """ label: NotRequired[str | None] """ Nicely formatted name of the instrument; if None, the ``name`` is used. """ + terminator: NotRequired[str | None] + """Read and write termination character(s).""" timeout: NotRequired[float] - "Seconds to allow for responses. Default 5." + "Seconds to allow for responses." device_clear: NotRequired[bool] - "Perform a device clear. Default True." + "Perform a device clear." visalib: NotRequired[str | None] """ Visa backend to use when connecting to this instrument. - This should be in the form of a string '@'. - Both parts can be omitted and pyvisa will try to infer the - path to the visa backend file. - By default the IVI backend is used if found, but '@py' will use the - ``pyvisa-py`` backend. Note that QCoDeS does not install (or even require) - ANY backends, it is up to the user to do that. see eg: - http://pyvisa.readthedocs.org/en/stable/names.html """ pyvisa_sim_file: NotRequired[str | None] """ Name of a pyvisa-sim yaml file used to simulate the instrument. - The file is expected to be loaded from a python module. - The file can be given either as only the file name in which case it is loaded - from ``qcodes.instruments.sims`` or in the format ``module:filename`` e.g. - ``qcodes.instruments.sims:AimTTi_PL601P.yaml`` in which case it is loaded - from the supplied module. Note that it is an error to pass both - ``pyvisa_sim_file`` and ``visalib``. """ @@ -100,8 +90,10 @@ class VisaInstrument(Instrument): Args: name: What this instrument is called locally. address: The visa resource name to use to connect. - timeout: seconds to allow for responses. Default 5. + timeout: seconds to allow for responses. If "unset" will read the value from + `self.default_timeout`. None means wait forever. Default 5. terminator: Read and write termination character(s). + If unset will use `self.default_terminator`. If None the terminator will not be set and we rely on the defaults from PyVisa. Default None. device_clear: Perform a device clear. Default True. @@ -129,17 +121,32 @@ class VisaInstrument(Instrument): """ + default_terminator: str | None = None + """ + The default terminator to use if the terminator is not specified when creating the instrument. + None means use the default terminator from PyVisa. + """ + default_timeout: float | None = 5 + """ + The default timeout in seconds if the timeout is not specified when creating the instrument. + None means no timeout e.g. wait forever. + """ + def __init__( self, name: str, address: str, - timeout: float = 5, - terminator: str | None = None, + timeout: float | None | Literal["Unset"] = "Unset", + terminator: str | None | Literal["Unset"] = "Unset", device_clear: bool = True, visalib: str | None = None, pyvisa_sim_file: str | None = None, **kwargs: Unpack[InstrumentBaseKWArgs], ): + if terminator == "Unset": + terminator = self.default_terminator + if timeout == "Unset": + timeout = self.default_timeout super().__init__(name, **kwargs) self.visa_log = get_instrument_logger(self, VISA_LOGGER) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py index b0001edcd6a..49333ce23b9 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8257D.py @@ -18,15 +18,16 @@ class AgilentE8257D(VisaInstrument): only the ones most commonly used. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, step_attenuator: Optional[bool] = None, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) if step_attenuator is not None: warnings.warn( From 2c31212f05251ea130f21ecb0ef37bbb18a40869 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 14 May 2024 20:51:03 +0200 Subject: [PATCH 17/23] Set terminator via attribute --- src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py | 7 ++----- src/qcodes/instrument_drivers/HP/HP_8133A.py | 5 +++-- src/qcodes/instrument_drivers/HP/HP_83650A.py | 3 +-- src/qcodes/instrument_drivers/HP/HP_8753D.py | 5 +++-- src/qcodes/instrument_drivers/Keithley/Keithley_2400.py | 5 +++-- src/qcodes/instrument_drivers/Keithley/Keithley_7510.py | 6 +++--- src/qcodes/instrument_drivers/Keithley/Keithley_s46.py | 6 +++--- src/qcodes/instrument_drivers/QDev/QDac_channels.py | 6 +----- src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py | 5 +++-- src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py | 5 +++-- .../instrument_drivers/american_magnetics/AMI430_visa.py | 5 ++--- src/qcodes/instrument_drivers/basel/BaselSP983a.py | 5 +++-- src/qcodes/instrument_drivers/stahl/stahl.py | 5 +++-- src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py | 6 +++--- 14 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py b/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py index e19e4ed5598..be2c6dab9cd 100644 --- a/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py +++ b/src/qcodes/instrument_drivers/AimTTi/_AimTTi_PL_P.py @@ -233,24 +233,21 @@ class AimTTi(VisaInstrument): "PL303QMT-P": 3, "QL355TP": 3, } + default_terminator = "\n" def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: """ Args: name: Name to use internally in QCoDeS. address: VISA resource address - terminator: Read and write termination character(s). - If None the terminator will not be set and we - rely on the defaults from PyVisa. Default None. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) channels = ChannelList(self, "Channels", AimTTiChannel, snapshotable=False) diff --git a/src/qcodes/instrument_drivers/HP/HP_8133A.py b/src/qcodes/instrument_drivers/HP/HP_8133A.py index b3b868262ca..f3707fc6628 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8133A.py +++ b/src/qcodes/instrument_drivers/HP/HP_8133A.py @@ -12,15 +12,16 @@ class HP8133A(VisaInstrument): QCoDeS driver for Hewlett Packard 8133A Pulse Generator. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, reset: bool = False, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ): - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( name="frequency", diff --git a/src/qcodes/instrument_drivers/HP/HP_83650A.py b/src/qcodes/instrument_drivers/HP/HP_83650A.py index 8edd9496b69..1a9451480d0 100644 --- a/src/qcodes/instrument_drivers/HP/HP_83650A.py +++ b/src/qcodes/instrument_drivers/HP/HP_83650A.py @@ -30,13 +30,12 @@ def __init__( verbose: int = 1, reset: bool = False, server_name: Optional[str] = None, - terminator: Optional[str] = None, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): self.verbose = verbose log.debug('Initializing instrument') - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter('frequency', label='Frequency', diff --git a/src/qcodes/instrument_drivers/HP/HP_8753D.py b/src/qcodes/instrument_drivers/HP/HP_8753D.py index 7ccfaa25cae..400cd9a6a57 100644 --- a/src/qcodes/instrument_drivers/HP/HP_8753D.py +++ b/src/qcodes/instrument_drivers/HP/HP_8753D.py @@ -115,14 +115,15 @@ class HP8753D(VisaInstrument): QCoDeS driver for the Hewlett Packard 8753D Network Analyzer. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( "start_freq", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py index 0ef9084d34e..5d2fe8325ae 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2400.py @@ -13,14 +13,15 @@ class Keithley2400(VisaInstrument): QCoDeS driver for the Keithley 2400 voltage source. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ): - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( "rangev", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py index 03801dda100..a1941aa2b32 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_7510.py @@ -689,11 +689,12 @@ class Keithley7510(VisaInstrument): The QCoDeS driver for the Keithley 7510 DMM """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ @@ -702,10 +703,9 @@ def __init__( Args: name: Name of the instrument instance address: Visa-resolvable instrument address - terminator: Character to terminate messages with. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( "sense_function", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py index ebfa4b77d8a..019e1ed2f43 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_s46.py @@ -143,15 +143,15 @@ class KeithleyS46(VisaInstrument): # Make a reverse dict for efficient alias lookup given a channel number aliases: ClassVar[dict[int, str]] = {v: k for k, v in channel_numbers.items()} + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ): - - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) try: self.add_parameter( "closed_channels", diff --git a/src/qcodes/instrument_drivers/QDev/QDac_channels.py b/src/qcodes/instrument_drivers/QDev/QDac_channels.py index d27a2db93e6..5d2d082f9cf 100644 --- a/src/qcodes/instrument_drivers/QDev/QDac_channels.py +++ b/src/qcodes/instrument_drivers/QDev/QDac_channels.py @@ -190,7 +190,6 @@ def __init__( address: str, num_chans: int = 48, update_currents: bool = True, - terminator: Optional[str] = None, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ @@ -202,15 +201,12 @@ def __init__( num_chans: Number of channels to assign. Default: 48 update_currents: Whether to query all channels for their current current value on startup. Default: True. - terminator: Read and write termination character(s). - If None the terminator will not be set and we - rely on the defaults from PyVisa. Default None. **kwargs: kwargs are forwarded to base class. Returns: QDac object """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self._output_n_lines = 50 handle = self.visa_handle assert isinstance(handle, SerialInstrument) diff --git a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py index dc0995f7147..9acb619e7c9 100644 --- a/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py +++ b/src/qcodes/instrument_drivers/agilent/Agilent_E8267C.py @@ -13,14 +13,15 @@ class AgilentE8267C(VisaInstrument): This is the QCoDeS driver for the Agilent E8267C signal generator. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) # general commands self.add_parameter( name="frequency", diff --git a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py index 6ad5412d704..df7e3484977 100644 --- a/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py +++ b/src/qcodes/instrument_drivers/agilent/_Agilent_344xxA.py @@ -17,14 +17,15 @@ class _Agilent344xxA(VisaInstrument): Note that most models are better supported by the Keysight 33xxA drivers. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) idn = self.IDN.get() self.model = idn["model"] diff --git a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py index 9709a734023..896cba536a0 100644 --- a/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py +++ b/src/qcodes/instrument_drivers/american_magnetics/AMI430_visa.py @@ -153,12 +153,13 @@ class AMIModel430(VisaInstrument): _RETRY_WRITE_ASK = True _RETRY_TIME = 5 + default_terminator = "\r\n" + def __init__( self, name: str, address: str, reset: bool = False, - terminator: str = "\r\n", current_ramp_limit: float | None = None, **kwargs: Unpack[VisaInstrumentKWArgs], ): @@ -176,7 +177,6 @@ def __init__( Of the form ``TCPIP[board]::host address::port::SOCKET`` e.g. ``TCPIP0::192.168.0.1::7800::SOCKET`` reset: Should the reset method be called on the instrument at init time - terminator: Read and write termination character(s). current_ramp_limit: A current ramp limit, in units of A/s **kwargs: Additional kwargs are passed to the base class """ @@ -192,7 +192,6 @@ def __init__( super().__init__( name, address, - terminator=terminator, **kwargs, ) diff --git a/src/qcodes/instrument_drivers/basel/BaselSP983a.py b/src/qcodes/instrument_drivers/basel/BaselSP983a.py index d8c6b291f9f..67ba2ef260f 100644 --- a/src/qcodes/instrument_drivers/basel/BaselSP983a.py +++ b/src/qcodes/instrument_drivers/basel/BaselSP983a.py @@ -26,15 +26,16 @@ class BaselSP983a(VisaInstrument): **kwargs: Forwarded to base class. """ + default_terminator = "\r\n" + def __init__( self, name: str, address: str, input_offset_voltage: Optional[Parameter] = None, - terminator: str = "\r\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.connect_message() diff --git a/src/qcodes/instrument_drivers/stahl/stahl.py b/src/qcodes/instrument_drivers/stahl/stahl.py index 64d28760ade..8d3aab32a52 100644 --- a/src/qcodes/instrument_drivers/stahl/stahl.py +++ b/src/qcodes/instrument_drivers/stahl/stahl.py @@ -165,14 +165,15 @@ class Stahl(VisaInstrument): In this case the VISA address would be: ``"TCPIP0::hostname::8088::SOCKET"`` """ + default_terminator = "\r" + def __init__( self, name: str, address: str, - terminator: Optional[str] = "\r", **kwargs: "Unpack[VisaInstrumentKWArgs]", ): - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) if isinstance(self.visa_handle, TCPIPSocket): pass # allow connection to remote serial device elif isinstance(self.visa_handle, SerialInstrument): diff --git a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py index 4d9d1f2d963..7414025e732 100644 --- a/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py +++ b/src/qcodes/instrument_drivers/yokogawa/Yokogawa_GS200.py @@ -268,17 +268,17 @@ class YokogawaGS200(VisaInstrument): name: What this instrument is called locally. address: The GPIB or USB address of this instrument kwargs: kwargs to be passed to VisaInstrument class - terminator: read terminator for reads/writes to the instrument. """ + default_terminator = "\n" + def __init__( self, name: str, address: str, - terminator: str = "\n", **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( "output", From 33eb2c8c3dd255e62688a5dc2973dd625a694a13 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 14 May 2024 21:06:50 +0200 Subject: [PATCH 18/23] Port Keithley to VisaKWargs dict --- .../Keithley/Keithley_2000.py | 19 +++++++++++++++---- .../Keithley/Keithley_2450.py | 11 +++++++---- .../Keithley/Keithley_3706A.py | 17 ++++++++++++----- .../Keithley/Keithley_6500.py | 17 +++++++++++++---- .../Keithley/_Keithley_2600.py | 16 +++++++++++++--- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2000.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2000.py index e4e179b84af..5c09c3c07fb 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2000.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2000.py @@ -1,9 +1,12 @@ from functools import partial -from typing import Any, Callable, Union +from typing import TYPE_CHECKING, Any, Callable, Union -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.validators import Bool, Enum, Ints, MultiType, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + def _parse_output_string(s: str) -> str: """Parses and cleans string outputs of the Keithley""" @@ -37,8 +40,16 @@ class Keithley2000(VisaInstrument): Driver for the Keithley 2000 multimeter. """ - def __init__(self, name: str, address: str, reset: bool = False, **kwargs: Any): - super().__init__(name, address, terminator="\n", **kwargs) + default_terminator = "\n" + + def __init__( + self, + name: str, + address: str, + reset: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): + super().__init__(name, address, **kwargs) self._trigger_sent = False diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_2450.py b/src/qcodes/instrument_drivers/Keithley/Keithley_2450.py index 3302315758d..4d3bdf12585 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_2450.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_2450.py @@ -1,9 +1,9 @@ from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast import numpy as np -from typing_extensions import TypedDict +from typing_extensions import TypedDict, Unpack -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import InstrumentChannel, VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import ( ParameterWithSetpoints, create_on_off_val_mapping, @@ -564,9 +564,12 @@ class Keithley2450(VisaInstrument): The QCoDeS driver for the Keithley 2450 SMU """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: + default_terminator = "\n" - super().__init__(name, address, terminator="\n", **kwargs) + def __init__( + self, name: str, address: str, **kwargs: Unpack[VisaInstrumentKWArgs] + ) -> None: + super().__init__(name, address, **kwargs) if not self._has_correct_language_mode(): self.log.warning( diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_3706A.py b/src/qcodes/instrument_drivers/Keithley/Keithley_3706A.py index c7923ff138f..41b1637d50b 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_3706A.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_3706A.py @@ -1,12 +1,15 @@ import itertools import textwrap import warnings -from typing import Any, Optional, Union +from typing import TYPE_CHECKING, Optional, Union import qcodes.validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import create_on_off_val_mapping +if TYPE_CHECKING: + from typing_extensions import Unpack + class Keithley3706AUnknownOrEmptySlot(Exception): pass @@ -22,17 +25,21 @@ class Keithley3706A(VisaInstrument): System Switch. """ + default_terminator = "\n" + def __init__( - self, name: str, address: str, terminator: str = "\n", **kwargs: Any + self, + name: str, + address: str, + **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: """ Args: name: Name to use internally in QCoDeS address: VISA resource address - terminator: Character to terminate messages with. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self.add_parameter( "channel_connect_rule", diff --git a/src/qcodes/instrument_drivers/Keithley/Keithley_6500.py b/src/qcodes/instrument_drivers/Keithley/Keithley_6500.py index 58aca842878..b8e01328cad 100644 --- a/src/qcodes/instrument_drivers/Keithley/Keithley_6500.py +++ b/src/qcodes/instrument_drivers/Keithley/Keithley_6500.py @@ -1,9 +1,12 @@ from functools import partial -from typing import Any, Callable, TypeVar, Union +from typing import TYPE_CHECKING, Callable, TypeVar, Union -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.validators import Bool, Enum, Ints, MultiType, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + T = TypeVar("T") @@ -45,8 +48,14 @@ class Keithley6500CommandSetError(Exception): class Keithley6500(VisaInstrument): + default_terminator = "\n" + def __init__( - self, name: str, address: str, reset_device: bool = False, **kwargs: Any + self, + name: str, + address: str, + reset_device: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """Driver for the Keithley 6500 multimeter. Based on the Keithley 2000 driver, commands have been adapted for the Keithley 6500. This driver does not contain @@ -60,7 +69,7 @@ def __init__( reset_device: Reset the device on startup if true. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator="\n", **kwargs) + super().__init__(name, address, **kwargs) command_set = self.ask("*LANG?") if command_set != "SCPI": diff --git a/src/qcodes/instrument_drivers/Keithley/_Keithley_2600.py b/src/qcodes/instrument_drivers/Keithley/_Keithley_2600.py index 151407a909b..b5785bc6e5a 100644 --- a/src/qcodes/instrument_drivers/Keithley/_Keithley_2600.py +++ b/src/qcodes/instrument_drivers/Keithley/_Keithley_2600.py @@ -10,7 +10,12 @@ import numpy as np import qcodes.validators as vals -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import ( ArrayParameter, Parameter, @@ -23,6 +28,7 @@ from collections.abc import Sequence from qcodes_loop.data.data_set import DataSet + from typing_extensions import Unpack if sys.version_info >= (3, 11): @@ -785,14 +791,18 @@ class Keithley2600(VisaInstrument): """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: Unpack[VisaInstrumentKWArgs] + ) -> None: """ Args: name: Name to use internally in QCoDeS address: VISA resource address **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator="\n", **kwargs) + super().__init__(name, address, **kwargs) model = self.ask("localnode.model") From 77b808c6e118c89223290148bcfb5400d0ed56bb Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 14 May 2024 21:34:53 +0200 Subject: [PATCH 19/23] Keysight before B models --- .../instrument_drivers/Keysight/Infiniium.py | 40 +++++++++++++---- .../Keysight/Keysight_34410A_submodules.py | 17 +++++-- .../Keysight/Keysight_34411A_submodules.py | 17 +++++-- .../Keysight/Keysight_34460A_submodules.py | 17 +++++-- .../Keysight/Keysight_34461A_submodules.py | 17 +++++-- .../Keysight/Keysight_34465A_submodules.py | 17 +++++-- .../Keysight/Keysight_34470A_submodules.py | 17 +++++-- .../Keysight/_Keysight_N5232B.py | 11 ++++- .../Keysight/keysight_34980a.py | 21 +++++---- .../Keysight/keysight_34980a_submodules.py | 19 ++++---- .../keysightb1500/KeysightB1500_base.py | 12 +++-- .../keysightb1500/KeysightB1500_module.py | 8 ++-- .../Keysight/keysightb1500/KeysightB1517A.py | 13 ++++-- .../Keysight/keysightb1500/KeysightB1520A.py | 11 ++++- .../Keysight/keysightb1500/KeysightB1530A.py | 7 ++- .../private/Keysight_344xxA_submodules.py | 44 ++++++++++++++++--- 16 files changed, 221 insertions(+), 67 deletions(-) diff --git a/src/qcodes/instrument_drivers/Keysight/Infiniium.py b/src/qcodes/instrument_drivers/Keysight/Infiniium.py index 6778981d1cd..ca207da7a27 100644 --- a/src/qcodes/instrument_drivers/Keysight/Infiniium.py +++ b/src/qcodes/instrument_drivers/Keysight/Infiniium.py @@ -9,9 +9,11 @@ from qcodes.instrument import ( ChannelList, InstrumentBase, + InstrumentBaseKWArgs, InstrumentChannel, InstrumentModule, VisaInstrument, + VisaInstrumentKWArgs, ) from qcodes.parameters import ( Parameter, @@ -23,6 +25,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + class DSOTimeAxisParam(Parameter): """ @@ -244,7 +248,12 @@ class AbstractMeasurementSubsystem(InstrumentModule): the measurement value. """ - def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: + def __init__( + self, + parent: InstrumentBase, + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: """ Add parameters to measurement subsystem. Note: This should not be initialized directly, rather initialize BoundMeasurementSubsystem @@ -458,7 +467,7 @@ def __init__( self, parent: Union["KeysightInfiniiumChannel", "KeysightInfiniiumFunction"], name: str, - **kwargs: Any, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ): """ Initialize measurement subsystem bound to a specific channel @@ -477,7 +486,12 @@ def __init__( class KeysightInfiniiumUnboundMeasurement(AbstractMeasurementSubsystem): - def __init__(self, parent: "KeysightInfiniium", name: str, **kwargs: Any): + def __init__( + self, + parent: "KeysightInfiniium", + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): """ Initialize measurement subsystem where target is set by the parameter `source`. """ @@ -553,7 +567,11 @@ def _get_source(self) -> str: class KeysightInfiniiumFunction(InstrumentChannel): def __init__( - self, parent: "KeysightInfiniium", name: str, channel: int, **kwargs: Any + self, + parent: "KeysightInfiniium", + name: str, + channel: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ): """ Initialize an infiniium channel. @@ -671,7 +689,11 @@ def _get_func(self) -> str: class KeysightInfiniiumChannel(InstrumentChannel): def __init__( - self, parent: "KeysightInfiniium", name: str, channel: int, **kwargs: Any + self, + parent: "KeysightInfiniium", + name: str, + channel: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ): """ Initialize an infiniium channel. @@ -777,14 +799,16 @@ class KeysightInfiniium(VisaInstrument): This is the QCoDeS driver for the Keysight Infiniium oscilloscopes """ + default_timeout = 20 + default_terminator = "\n" + def __init__( self, name: str, address: str, - timeout: float = 20, channels: int = 4, silence_pyvisapy_warning: bool = False, - **kwargs: Any, + **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ Initialises the oscilloscope. @@ -797,7 +821,7 @@ def __init__( silence_pyvisapy_warning: Don't warn about pyvisa-py at startup **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, timeout=timeout, terminator="\n", **kwargs) + super().__init__(name, address, **kwargs) self.connect_message() # Check if we are using pyvisa-py as our visa lib and warn users that diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34410A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34410A_submodules.py index 8abff1008ee..1ad68b30e17 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34410A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34410A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34410A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34410A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34411A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34411A_submodules.py index 75778aaaad7..17e8e736653 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34411A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34411A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34411A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34411A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34460A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34460A_submodules.py index 3b7f929f340..6830396791d 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34460A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34460A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34460A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34460A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34461A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34461A_submodules.py index 0572233927f..ac2c5b18754 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34461A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34461A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34461A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34461A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34465A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34465A_submodules.py index 658d5971447..3f76a6806af 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34465A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34465A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34465A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34465A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_34470A_submodules.py b/src/qcodes/instrument_drivers/Keysight/Keysight_34470A_submodules.py index 37bde93bc69..df08ad835b9 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_34470A_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_34470A_submodules.py @@ -1,14 +1,25 @@ -from typing import Any +from typing import TYPE_CHECKING from .private.Keysight_344xxA_submodules import _Keysight_344xxA +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class Keysight34470A(_Keysight_344xxA): """ This is the qcodes driver for the Keysight 34470A Multimeter """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): super().__init__(name, address, silent, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/_Keysight_N5232B.py b/src/qcodes/instrument_drivers/Keysight/_Keysight_N5232B.py index 018b60d9bcd..a02e367ab26 100644 --- a/src/qcodes/instrument_drivers/Keysight/_Keysight_N5232B.py +++ b/src/qcodes/instrument_drivers/Keysight/_Keysight_N5232B.py @@ -1,10 +1,17 @@ -from typing import Any +from typing import TYPE_CHECKING from . import N52xx +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class KeysightN5232B(N52xx.PNABase): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): super().__init__( name, address, diff --git a/src/qcodes/instrument_drivers/Keysight/keysight_34980a.py b/src/qcodes/instrument_drivers/Keysight/keysight_34980a.py index 067126faaf2..11c1f46d59c 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysight_34980a.py +++ b/src/qcodes/instrument_drivers/Keysight/keysight_34980a.py @@ -2,14 +2,17 @@ import re import warnings from functools import wraps -from typing import Any, Callable, Optional, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar from qcodes import validators as vals -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from .keysight_34934a import Keysight34934A from .keysight_34980a_submodules import Keysight34980ASwitchMatrixSubModule +if TYPE_CHECKING: + from typing_extensions import Unpack + KEYSIGHT_MODELS = {'34934A': Keysight34934A} @@ -45,21 +48,21 @@ class Keysight34980A(VisaInstrument): """ QCodes driver for 34980A switch/measure unit """ - def __init__(self, - name: str, - address: str, - terminator: str = '\n', - **kwargs: Any): + + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): """ Create an instance of the instrument. Args: name: Name of the instrument instance address: Visa-resolvable instrument address. - terminator: Character to terminate messages with. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) self._total_slot = 8 self._system_slots_info_dict: Optional[dict[int, dict[str, str]]] = None diff --git a/src/qcodes/instrument_drivers/Keysight/keysight_34980a_submodules.py b/src/qcodes/instrument_drivers/Keysight/keysight_34980a_submodules.py index 2588a506824..6e8fc863ada 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysight_34980a_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/keysight_34980a_submodules.py @@ -1,6 +1,9 @@ -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import InstrumentBaseKWArgs, InstrumentChannel, VisaInstrument + +if TYPE_CHECKING: + from typing_extensions import Unpack class KeysightSubModule(InstrumentChannel): @@ -13,13 +16,13 @@ class KeysightSubModule(InstrumentChannel): slot: the slot the module is installed """ def __init__( - self, - parent: Union[VisaInstrument, InstrumentChannel], - name: str, - slot: int + self, + parent: Union[VisaInstrument, InstrumentChannel], + name: str, + slot: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: - - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) self.slot = slot diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py index 26ba9dcfb22..c57b5301718 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_base.py @@ -3,7 +3,7 @@ from collections import defaultdict from typing import TYPE_CHECKING, Any, Optional, Union -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import MultiParameter, create_on_off_val_mapping from . import constants @@ -25,6 +25,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + class KeysightB1500(VisaInstrument): """Driver for Keysight B1500 Semiconductor Parameter Analyzer. @@ -33,8 +35,12 @@ class KeysightB1500(VisaInstrument): """ calibration_time_out = 60 # 30 seconds suggested by manual - def __init__(self, name: str, address: str, **kwargs: Any): - super().__init__(name, address, terminator="\r\n", **kwargs) + default_terminator = "\r\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): + super().__init__(name, address, **kwargs) self.by_slot: dict[constants.SlotNr, B1500Module] = {} self.by_channel: dict[constants.ChNr, B1500Module] = {} self.by_kind: dict[constants.ModuleKind, list[B1500Module]] = defaultdict(list) diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_module.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_module.py index 5d988b767e3..210649ddd3a 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_module.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1500_module.py @@ -1,11 +1,11 @@ import re from collections import namedtuple -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Optional, Union, cast import numpy as np -from typing_extensions import TypedDict +from typing_extensions import TypedDict, Unpack -from qcodes.instrument import InstrumentChannel +from qcodes.instrument import InstrumentBaseKWArgs, InstrumentChannel from . import constants from .constants import ChannelName, ChNr, MeasurementStatus, ModuleKind, SlotNr @@ -289,7 +289,7 @@ def __init__( parent: "qcodes.instrument_drivers.Keysight.keysightb1500.KeysightB1500", name: Optional[str], slot_nr: int, - **kwargs: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], ): # self.channels will be populated in the concrete module subclasses # because channel count is module specific diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1517A.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1517A.py index 6e6a829620a..b272dcb506b 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1517A.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1517A.py @@ -3,10 +3,10 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast, overload import numpy as np -from typing_extensions import NotRequired, TypedDict +from typing_extensions import NotRequired, TypedDict, Unpack import qcodes.validators as vals -from qcodes.instrument import InstrumentChannel +from qcodes.instrument import InstrumentBaseKWArgs, InstrumentChannel from qcodes.parameters import Group, GroupParameter, Parameter, ParamRawDataType from . import constants @@ -50,7 +50,12 @@ class SweepSteps(TypedDict): class KeysightB1500IVSweeper(InstrumentChannel): - def __init__(self, parent: "KeysightB1517A", name: str, **kwargs: Any): + def __init__( + self, + parent: "KeysightB1517A", + name: str, + **kwargs: Unpack[InstrumentBaseKWArgs], + ): super().__init__(parent, name, **kwargs) self._sweep_step_parameters: SweepSteps = \ {"sweep_mode": constants.SweepMode.LINEAR, @@ -649,7 +654,7 @@ def __init__( parent: "KeysightB1500", name: Optional[str], slot_nr: int, - **kwargs: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], ): super().__init__(parent, name, slot_nr, **kwargs) self.channels = (ChNr(slot_nr),) diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py index bf21d76710c..7c8f2308076 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1520A.py @@ -5,7 +5,7 @@ import numpy as np import qcodes.validators as vals -from qcodes.instrument import InstrumentChannel +from qcodes.instrument import InstrumentBaseKWArgs, InstrumentChannel from qcodes.parameters import Group, GroupParameter, MultiParameter from . import constants @@ -26,6 +26,8 @@ from .message_builder import MessageBuilder if TYPE_CHECKING: + from typing_extensions import Unpack + from qcodes.instrument_drivers.Keysight.keysightb1500.KeysightB1500_base import ( KeysightB1500, ) @@ -35,7 +37,12 @@ class KeysightB1500CVSweeper(InstrumentChannel): - def __init__(self, parent: "KeysightB1520A", name: str, **kwargs: Any): + def __init__( + self, + parent: "KeysightB1520A", + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): super().__init__(parent, name, **kwargs) self.add_parameter(name='sweep_auto_abort', diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1530A.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1530A.py index 8b1b9843189..be97bddf071 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1530A.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1530A.py @@ -1,9 +1,12 @@ -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional from .constants import ChNr, ModuleKind from .KeysightB1500_module import B1500Module if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import InstrumentBaseKWArgs from qcodes.instrument_drivers.Keysight.keysightb1500.KeysightB1500_base import ( KeysightB1500, ) @@ -31,7 +34,7 @@ def __init__( parent: "KeysightB1500", name: Optional[str], slot_nr: int, - **kwargs: Any, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ): super().__init__(parent, name, slot_nr, **kwargs) 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 2130f2321f5..d4f804258f8 100644 --- a/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py +++ b/src/qcodes/instrument_drivers/Keysight/private/Keysight_344xxA_submodules.py @@ -8,7 +8,13 @@ from packaging import version import qcodes.validators as vals -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.instrument_drivers.Keysight.private.error_handling import ( KeysightErrorQueueMixin, ) @@ -18,11 +24,18 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + class Trigger(InstrumentChannel): """Implements triggering parameters and methods of Keysight 344xxA.""" - def __init__(self, parent: '_Keysight_344xxA', name: str, **kwargs: Any): + def __init__( + self, + parent: "_Keysight_344xxA", + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): super().__init__(parent, name, **kwargs) if self.parent.is_34465A_34470A: @@ -152,7 +165,12 @@ def force(self) -> None: class Sample(InstrumentChannel): """Implements sampling parameters of Keysight 344xxA.""" - def __init__(self, parent: '_Keysight_344xxA', name: str, **kwargs: Any): + def __init__( + self, + parent: "_Keysight_344xxA", + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): super().__init__(parent, name, **kwargs) if self.parent.is_34465A_34470A: @@ -273,7 +291,12 @@ def __init__(self, parent: '_Keysight_344xxA', name: str, **kwargs: Any): class Display(InstrumentChannel): """Implements interaction with the display of Keysight 344xxA.""" - def __init__(self, parent: '_Keysight_344xxA', name: str, **kwargs: Any): + def __init__( + self, + parent: "_Keysight_344xxA", + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ): super().__init__(parent, name, **kwargs) self.add_parameter('enabled', @@ -442,8 +465,15 @@ class _Keysight_344xxA(KeysightErrorQueueMixin, VisaInstrument): ranges: A list of the available voltage ranges """ - def __init__(self, name: str, address: str, silent: bool = False, - **kwargs: Any): + default_terminator = "\n" + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): """ Create an instance of the instrument. @@ -455,7 +485,7 @@ def __init__(self, name: str, address: str, silent: bool = False, **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator='\n', **kwargs) + super().__init__(name, address, **kwargs) idn = self.IDN.get() self.model = idn['model'] From 144c51bd990963cba44e3b2a591284d4ecf1bc4e Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 17 May 2024 11:31:49 +0200 Subject: [PATCH 20/23] use typed kwargs for remaining Keysight drivers --- .../Keysight/KeysightAgilent_33XXX.py | 38 ++++++++++++++---- .../Keysight/Keysight_B2962A.py | 34 +++++++++++++--- .../Keysight/Keysight_N5183B.py | 11 ++++- .../Keysight/Keysight_N5222B.py | 11 ++++- .../Keysight/Keysight_N5230C.py | 11 ++++- .../Keysight/Keysight_N5245A.py | 11 ++++- .../Keysight/Keysight_N6705B.py | 31 +++++++++++--- .../Keysight/Keysight_N9030B.py | 30 +++++++++++--- .../Keysight/Keysight_P9374A.py | 11 ++++- .../instrument_drivers/Keysight/KtM960x.py | 22 +++++----- .../instrument_drivers/Keysight/KtMAwg.py | 32 ++++++++++----- .../instrument_drivers/Keysight/N51x1.py | 20 ++++++++-- .../instrument_drivers/Keysight/N52xx.py | 40 +++++++++++++------ .../Keysight/keysight_34934a.py | 21 +++++++--- .../Keysight/keysight_b220x.py | 13 ++++-- .../Keysight/keysight_e4980a.py | 32 ++++++++++----- 16 files changed, 276 insertions(+), 92 deletions(-) diff --git a/src/qcodes/instrument_drivers/Keysight/KeysightAgilent_33XXX.py b/src/qcodes/instrument_drivers/Keysight/KeysightAgilent_33XXX.py index ec628305748..9bcaf6e6eed 100644 --- a/src/qcodes/instrument_drivers/Keysight/KeysightAgilent_33XXX.py +++ b/src/qcodes/instrument_drivers/Keysight/KeysightAgilent_33XXX.py @@ -1,12 +1,21 @@ import logging from functools import partial -from typing import Any, Union +from typing import TYPE_CHECKING, Union from qcodes import validators as vals -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from .private.error_handling import KeysightErrorQueueMixin +if TYPE_CHECKING: + from typing_extensions import Unpack + log = logging.getLogger(__name__) @@ -19,15 +28,23 @@ class Keysight33xxxOutputChannel(InstrumentChannel): """ Class to hold the output channel of a Keysight 33xxxx waveform generator. """ - def __init__(self, parent: Instrument, name: str, channum: int) -> None: + + def __init__( + self, + parent: Instrument, + name: str, + channum: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: """ Args: parent: The instrument to which the channel is attached. name: The name of the channel channum: The number of the channel in question (1-2) + **kwargs: kwargs are forwarded to base class. """ - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) def val_parser(parser: type, inputstring: str) -> Union[float,int]: """ @@ -288,8 +305,15 @@ class WaveformGenerator_33XXX(KeysightErrorQueueMixin, VisaInstrument): waveform generators """ - def __init__(self, name: str, address: str, - silent: bool = False, **kwargs: Any): + default_terminator = "\n" + + def __init__( + self, + name: str, + address: str, + silent: bool = False, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): """ Args: name: The name of the instrument used internally @@ -299,7 +323,7 @@ def __init__(self, name: str, address: str, **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator='\n', **kwargs) + super().__init__(name, address, **kwargs) self.model = self.IDN()['model'] ####################################################################### diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_B2962A.py b/src/qcodes/instrument_drivers/Keysight/Keysight_B2962A.py index 8159bc46478..a733274fa38 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_B2962A.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_B2962A.py @@ -1,18 +1,35 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) + +if TYPE_CHECKING: + from typing_extensions import Unpack class KeysightB2962AChannel(InstrumentChannel): """ """ - def __init__(self, parent: Instrument, name: str, chan: int) -> None: + + def __init__( + self, + parent: Instrument, + name: str, + chan: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: """ Args: parent: The instrument to which the channel is attached. name: The name of the channel chan: The number of the channel in question (1-2) + **kwargs: Forwarded to base class. """ # Sanity Check inputs if name not in ["ch1", "ch2"]: @@ -20,7 +37,7 @@ def __init__(self, parent: Instrument, name: str, chan: int) -> None: if chan not in [1, 2]: raise ValueError(f"Invalid Channel: {chan}, expected '1' or '2'") - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) self.add_parameter('source_voltage', label=f"Channel {chan} Voltage", @@ -96,8 +113,13 @@ class KeysightB2962A(VisaInstrument): - Similar drivers have special handlers to map return values of 9.9e+37 to inf, is this needed? """ - def __init__(self, name: str, address: str, **kwargs: Any): - super().__init__(name, address, terminator='\n', **kwargs) + + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): + super().__init__(name, address, **kwargs) # The B2962A supports two channels for ch_num in [1, 2]: diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N5183B.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N5183B.py index e17896bd3ac..7ab9523d316 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N5183B.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N5183B.py @@ -1,10 +1,17 @@ -from typing import Any +from typing import TYPE_CHECKING from qcodes.instrument_drivers.Keysight.N51x1 import N51x1 +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class KeysightN5183B(N51x1): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): super().__init__(name, address, min_power=-20, max_power=19, **kwargs) diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N5222B.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N5222B.py index f97fd8c4061..12219246e57 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N5222B.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N5222B.py @@ -1,10 +1,17 @@ -from typing import Any +from typing import TYPE_CHECKING from . import N52xx +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class KeysightN5222B(N52xx.PNABase): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): """Driver for Keysight PNA N5222B.""" super().__init__( name, diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N5230C.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N5230C.py index 7a752ac263f..e9ca7764236 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N5230C.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N5230C.py @@ -1,10 +1,17 @@ -from typing import Any +from typing import TYPE_CHECKING from . import N52xx +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class KeysightN5230C(N52xx.PNABase): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): super().__init__( name, address, diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N5245A.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N5245A.py index 2b21c406dee..f59a9da91f4 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N5245A.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N5245A.py @@ -1,10 +1,17 @@ -from typing import Any +from typing import TYPE_CHECKING from . import N52xx +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + class KeysightN5245A(N52xx.PNAxBase): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): super().__init__( name, address, diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N6705B.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N6705B.py index 667f9420eae..b6fa775787c 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N6705B.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N6705B.py @@ -1,14 +1,29 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import Instrument, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + Instrument, + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) + +if TYPE_CHECKING: + from typing_extensions import Unpack class KeysightN6705BChannel(InstrumentChannel): - def __init__(self, parent: Instrument, name: str, chan: int) -> None: + def __init__( + self, + parent: Instrument, + name: str, + chan: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: if chan not in [1, 2, 3, 4]: raise ValueError('Invalid channel specified') - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) self.add_parameter('source_voltage', label=f"Channel {chan} Voltage", @@ -62,8 +77,12 @@ def __init__(self, parent: Instrument, name: str, chan: int) -> None: class KeysightN6705B(VisaInstrument): - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator="\n", **kwargs) + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ) -> None: + super().__init__(name, address, **kwargs) self.channels: list[KeysightN6705BChannel] = [] for ch_num in [1, 2, 3, 4]: ch_name = f"ch{ch_num}" diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py b/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py index ade26b536fc..ee06a76cd32 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_N9030B.py @@ -1,10 +1,15 @@ from __future__ import annotations -from typing import Any, Callable +from typing import TYPE_CHECKING, Any, Callable import numpy as np -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import ( Parameter, ParameterWithSetpoints, @@ -13,6 +18,9 @@ ) from qcodes.validators import Arrays, Bool, Enum, Ints, Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + class FrequencyAxis(Parameter): def __init__( @@ -73,7 +81,7 @@ def __init__( name: str, *arg: Any, additional_wait: int = 1, - **kwargs: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], ): super().__init__(parent, name, *arg, **kwargs) @@ -411,7 +419,13 @@ class KeysightN9030BPhaseNoiseMode(InstrumentChannel): Phase Noise Mode for Keysight N9030B instrument. """ - def __init__(self, parent: KeysightN9030B, name: str, *arg: Any, **kwargs: Any): + def __init__( + self, + parent: KeysightN9030B, + name: str, + *arg: Any, + **kwargs: Unpack[InstrumentBaseKWArgs], + ): super().__init__(parent, name, *arg, **kwargs) self._min_freq = 1 @@ -611,8 +625,12 @@ class KeysightN9030B(VisaInstrument): address """ - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator="\n", **kwargs) + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: Unpack[VisaInstrumentKWArgs] + ) -> None: + super().__init__(name, address, **kwargs) self._min_freq: float self._max_freq: float diff --git a/src/qcodes/instrument_drivers/Keysight/Keysight_P9374A.py b/src/qcodes/instrument_drivers/Keysight/Keysight_P9374A.py index 87025cbc863..27164a968dc 100644 --- a/src/qcodes/instrument_drivers/Keysight/Keysight_P9374A.py +++ b/src/qcodes/instrument_drivers/Keysight/Keysight_P9374A.py @@ -1,12 +1,19 @@ -from typing import Any +from typing import TYPE_CHECKING from . import N52xx +if TYPE_CHECKING: + from typing_extensions import Unpack + + from qcodes.instrument import VisaInstrumentKWArgs + # This is not the same class of Keysight devices but seems to work well... class KeysightP9374A(N52xx.PNAxBase): - def __init__(self, name: str, address: str, **kwargs: Any): + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): super().__init__( name, address, diff --git a/src/qcodes/instrument_drivers/Keysight/KtM960x.py b/src/qcodes/instrument_drivers/Keysight/KtM960x.py index 044c381d0ce..c9c12bf28cd 100644 --- a/src/qcodes/instrument_drivers/Keysight/KtM960x.py +++ b/src/qcodes/instrument_drivers/Keysight/KtM960x.py @@ -1,9 +1,9 @@ import ctypes from functools import partial -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional import qcodes.validators as vals -from qcodes.instrument import Instrument +from qcodes.instrument import Instrument, InstrumentBaseKWArgs from qcodes.parameters import ( MultiParameter, ParamRawDataType, @@ -12,6 +12,9 @@ from .KtM960xDefs import * # noqa F403 +if TYPE_CHECKING: + from typing_extensions import Unpack + class Measure(MultiParameter): def __init__(self, name: str, instrument: "KeysightM960x") -> None: @@ -40,13 +43,14 @@ class KeysightM960x(Instrument): _default_buf_size = 256 - def __init__(self, - name: str, - address: str, - options: str = "", - dll_path: str = r"C:\Program Files\IVI " - r"Foundation\IVI\Bin\KtM960x_64.dll", - **kwargs: Any) -> None: + def __init__( + self, + name: str, + address: str, + options: str = "", + dll_path: str = r"C:\Program Files\IVI Foundation\IVI\Bin\KtM960x_64.dll", + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: super().__init__(name, **kwargs) self._address = bytes(address, "ascii") diff --git a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py index 0459898752e..c16e13315ec 100644 --- a/src/qcodes/instrument_drivers/Keysight/KtMAwg.py +++ b/src/qcodes/instrument_drivers/Keysight/KtMAwg.py @@ -1,13 +1,16 @@ import ctypes from functools import partial -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import Instrument, InstrumentChannel +from qcodes.instrument import Instrument, InstrumentBaseKWArgs, InstrumentChannel from qcodes.parameters import create_on_off_val_mapping from qcodes.validators import Numbers from .KtMAwgDefs import * # noqa F403 +if TYPE_CHECKING: + from typing_extensions import Unpack + class KeysightM9336AAWGChannel(InstrumentChannel): """ @@ -16,14 +19,20 @@ class KeysightM9336AAWGChannel(InstrumentChannel): seperate waveforms. """ - def __init__(self, parent: "KeysightM9336A", name: str, chan: int) -> None: + def __init__( + self, + parent: "KeysightM9336A", + name: str, + chan: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: # Sanity Check inputs if name not in ["ch1", "ch2", "ch3"]: raise ValueError(f"Invalid channel: {name}, expecting ch1:ch3") if chan not in [1, 2, 3]: raise ValueError(f"Invalid channel: {chan}, expecting ch1:ch3") - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) self._channel = ctypes.create_string_buffer( f"Channel{chan}".encode("ascii") ) @@ -225,13 +234,14 @@ class KeysightM9336A(Instrument): """ _default_buf_size = 256 - def __init__(self, - name: str, - address: str, - options: str = "", - dll_path: str = r"C:\Program Files\IVI " - r"Foundation\IVI\Bin\KtMAwg_64.dll", - **kwargs: Any) -> None: + def __init__( + self, + name: str, + address: str, + options: str = "", + dll_path: str = r"C:\Program Files\IVI Foundation\IVI\Bin\KtMAwg_64.dll", + **kwargs: "Unpack[InstrumentBaseKWArgs]", + ) -> None: super().__init__(name, **kwargs) self._address = bytes(address, "ascii") diff --git a/src/qcodes/instrument_drivers/Keysight/N51x1.py b/src/qcodes/instrument_drivers/Keysight/N51x1.py index 5d1fbda2d0a..0cd5000b63b 100644 --- a/src/qcodes/instrument_drivers/Keysight/N51x1.py +++ b/src/qcodes/instrument_drivers/Keysight/N51x1.py @@ -1,9 +1,12 @@ -from typing import Any, Optional +from typing import TYPE_CHECKING, Optional -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.parameters import create_on_off_val_mapping from qcodes.validators import Numbers +if TYPE_CHECKING: + from typing_extensions import Unpack + class N51x1(VisaInstrument): """ @@ -11,8 +14,17 @@ class N51x1(VisaInstrument): It has been tested with N5171B, N5181A, N5173B, N5183B """ - def __init__(self, name: str, address: str, min_power: int = -144, max_power: int = 19, **kwargs: Any): - super().__init__(name, address, terminator='\n', **kwargs) + default_terminator = "\n" + + def __init__( + self, + name: str, + address: str, + min_power: int = -144, + max_power: int = 19, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ): + super().__init__(name, address, **kwargs) self._options = self.ask("*OPT?") # Determine installed frequency option diff --git a/src/qcodes/instrument_drivers/Keysight/N52xx.py b/src/qcodes/instrument_drivers/Keysight/N52xx.py index 8b6547b6e15..7f5a57594b5 100644 --- a/src/qcodes/instrument_drivers/Keysight/N52xx.py +++ b/src/qcodes/instrument_drivers/Keysight/N52xx.py @@ -5,7 +5,13 @@ import numpy as np from pyvisa import constants, errors -from qcodes.instrument import ChannelList, InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + ChannelList, + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import ( Parameter, ParameterBase, @@ -17,6 +23,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + class PNAAxisParameter(Parameter): def __init__( @@ -142,7 +150,7 @@ def __init__( port: int, min_power: Union[int, float], max_power: Union[int, float], - **kwargs: Any, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: super().__init__(parent, name, **kwargs) @@ -185,7 +193,7 @@ def __init__( name: str, trace_name: str, trace_num: int, - **kwargs: Any, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: super().__init__(parent, name, **kwargs) self.trace_name = trace_name @@ -384,16 +392,22 @@ class PNABase(VisaInstrument): may have unexpected results. """ - def __init__(self, - name: str, - address: str, - # Set frequency ranges - min_freq: Union[int, float], max_freq: Union[int, float], - # Set power ranges - min_power: Union[int, float], max_power: Union[int, float], - nports: int, # Number of ports on the PNA - **kwargs: Any) -> None: - super().__init__(name, address, terminator='\n', **kwargs) + default_terminator = "\n" + + def __init__( + self, + name: str, + address: str, + # Set frequency ranges + min_freq: Union[int, float], + max_freq: Union[int, float], + # Set power ranges + min_power: Union[int, float], + max_power: Union[int, float], + nports: int, # Number of ports on the PNA + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ) -> None: + super().__init__(name, address, **kwargs) self.min_freq = min_freq self.max_freq = max_freq diff --git a/src/qcodes/instrument_drivers/Keysight/keysight_34934a.py b/src/qcodes/instrument_drivers/Keysight/keysight_34934a.py index 6249c5765dc..cfede2ef0e7 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysight_34934a.py +++ b/src/qcodes/instrument_drivers/Keysight/keysight_34934a.py @@ -4,13 +4,19 @@ from qcodes import validators -from .keysight_34980a_submodules import KeysightSwitchMatrixSubModule +from .keysight_34980a_submodules import Keysight34980ASwitchMatrixSubModule if TYPE_CHECKING: - from qcodes.instrument import InstrumentChannel, VisaInstrument + from typing_extensions import Unpack + from qcodes.instrument import ( + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + ) -class Keysight34934A(KeysightSwitchMatrixSubModule): + +class Keysight34934A(Keysight34980ASwitchMatrixSubModule): """ Create an instance for module 34933A. @@ -20,10 +26,13 @@ class Keysight34934A(KeysightSwitchMatrixSubModule): slot: the slot the module is installed """ def __init__( - self, parent: Union["VisaInstrument", "InstrumentChannel"], name: str, slot: int + self, + parent: Union["VisaInstrument", "InstrumentChannel"], + name: str, + slot: int, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: - - super().__init__(parent, name, slot) + super().__init__(parent, name, slot, **kwargs) self.add_parameter( name="protection_mode", diff --git a/src/qcodes/instrument_drivers/Keysight/keysight_b220x.py b/src/qcodes/instrument_drivers/Keysight/keysight_b220x.py index c608c99cd29..7700c889222 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysight_b220x.py +++ b/src/qcodes/instrument_drivers/Keysight/keysight_b220x.py @@ -3,12 +3,15 @@ from functools import wraps from typing import TYPE_CHECKING, Any, Callable, TypeVar -from qcodes.instrument import VisaInstrument +from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs from qcodes.validators import Enum, Ints, Lists, MultiType if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + + T = TypeVar('T') @@ -54,8 +57,12 @@ class KeysightB220X(VisaInstrument): _available_input_ports = Ints(1, 14) _available_output_ports = Ints(1, 48) - def __init__(self, name: str, address: str, **kwargs: Any): - super().__init__(name, address, terminator='\n', **kwargs) + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): + super().__init__(name, address, **kwargs) self._card = 0 diff --git a/src/qcodes/instrument_drivers/Keysight/keysight_e4980a.py b/src/qcodes/instrument_drivers/Keysight/keysight_e4980a.py index 427a7229289..6c55e49cb75 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysight_e4980a.py +++ b/src/qcodes/instrument_drivers/Keysight/keysight_e4980a.py @@ -3,7 +3,12 @@ from packaging import version from pyvisa.errors import VisaIOError -from qcodes.instrument import InstrumentChannel, VisaInstrument +from qcodes.instrument import ( + InstrumentBaseKWArgs, + InstrumentChannel, + VisaInstrument, + VisaInstrumentKWArgs, +) from qcodes.parameters import ( Group, GroupParameter, @@ -18,6 +23,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Unpack + class KeysightE4980AMeasurementPair(MultiParameter): """ @@ -144,12 +151,14 @@ class KeysightE4980ACorrection(InstrumentChannel): """ Module for correction settings. """ + def __init__( - self, - parent: VisaInstrument, - name: str, + self, + parent: VisaInstrument, + name: str, + **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: - super().__init__(parent, name) + super().__init__(parent, name, **kwargs) self.add_parameter( "open", @@ -188,11 +197,12 @@ class KeysightE4980A(VisaInstrument): """ QCodes driver for E4980A Precision LCR Meter """ - def __init__(self, - name: str, - address: str, - terminator: str = '\n', - **kwargs: Any): + + default_terminator = "\n" + + def __init__( + self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" + ): """ Create an instance of the instrument. @@ -202,7 +212,7 @@ def __init__(self, terminator: Character to terminate messages with. **kwargs: kwargs are forwarded to base class. """ - super().__init__(name, address, terminator=terminator, **kwargs) + super().__init__(name, address, **kwargs) idn = self.IDN.get() From 7620f47645916cb91c0086419e26166126da566c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 17 May 2024 13:38:42 +0200 Subject: [PATCH 21/23] remove redundant union[int,float] This is equivalent to just float --- .../instrument_drivers/Keysight/N52xx.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/qcodes/instrument_drivers/Keysight/N52xx.py b/src/qcodes/instrument_drivers/Keysight/N52xx.py index 7f5a57594b5..402bbeca7bf 100644 --- a/src/qcodes/instrument_drivers/Keysight/N52xx.py +++ b/src/qcodes/instrument_drivers/Keysight/N52xx.py @@ -1,6 +1,6 @@ import re import time -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any import numpy as np from pyvisa import constants, errors @@ -148,8 +148,8 @@ def __init__( parent: "PNABase", name: str, port: int, - min_power: Union[int, float], - max_power: Union[int, float], + min_power: float, + max_power: float, **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: super().__init__(parent, name, **kwargs) @@ -168,9 +168,7 @@ def __init__( vals=Numbers(min_value=min_power, max_value=max_power)) - def _set_power_limits(self, - min_power: Union[int, float], - max_power: Union[int, float]) -> None: + def _set_power_limits(self, min_power: float, max_power: float) -> None: """ Set port power limits """ @@ -399,11 +397,11 @@ def __init__( name: str, address: str, # Set frequency ranges - min_freq: Union[int, float], - max_freq: Union[int, float], + min_freq: float, + max_freq: float, # Set power ranges - min_power: Union[int, float], - max_power: Union[int, float], + min_power: float, + max_power: float, nports: int, # Number of ports on the PNA **kwargs: "Unpack[VisaInstrumentKWArgs]", ) -> None: @@ -724,9 +722,7 @@ def averages_off(self) -> None: """ self.averages_enabled(False) - def _set_power_limits(self, - min_power: Union[int, float], - max_power: Union[int, float]) -> None: + def _set_power_limits(self, min_power: float, max_power: float) -> None: """ Set port power limits """ From 2bad66b3cfbdf9fd7decd97320c984a9e98a65b3 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 17 May 2024 13:48:18 +0200 Subject: [PATCH 22/23] update docs to use default terminator arg --- .../Creating-Instrument-Drivers.ipynb | 20 +++++++++++-------- ...reating-Simulated-PyVISA-Instruments.ipynb | 5 +++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb index 25d304f4415..6334bda1912 100644 --- a/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb +++ b/docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb @@ -209,11 +209,15 @@ " # all instrument constructors should accept **kwargs and pass them on to\n", " # super().__init__ By using Unpack[VisaKWArgs] we ensure that the instrument class takes exactly the same keyword args as the base class\n", " # Visa instruments are also required to take name, address and terminator as arguments.\n", - " # name and address should be positional arguments and terminator a keyword argument with the default value set to the terminator that the\n", - " # instrument expects (check the instrument manual)\n", - " def __init__(self, name: str, address: str, terminator: str = \"\\r\", **kwargs: \"Unpack[VisaInstrumentKWArgs]\"):\n", - " # supplying the terminator means you don't need to remove it from every response\n", - " super().__init__(name, address, terminator=terminator, **kwargs)\n", + " # name and address should be positional arguments. To overwrite the default terminator or timeout\n", + " # the attribute default_terminator or default_timeout should be defined as a class attribute in the instrument class\n", + " # as shown below for the default_terminator\n", + " default_terminator = \"\\r\"\n", + "\n", + " def __init__(\n", + " self, name: str, address: str, **kwargs: \"Unpack[VisaInstrumentKWArgs]\"\n", + " ):\n", + " super().__init__(name, address, **kwargs)\n", "\n", " self.attenuation = Parameter(\n", " \"attenuation\",\n", @@ -357,16 +361,16 @@ " This is the qcodes driver for the Keithley2600 Source-Meter series,\n", " tested with Keithley2614B\n", " \"\"\"\n", + " default_terminator = \"\\n\"\n", "\n", - " def __init__(self, name: str, address: str, terminator=\"\\n\", **kwargs: \"Unpack[VisaInstrumentKWArgs]\") -> None:\n", + " def __init__(self, name: str, address: str, **kwargs: \"Unpack[VisaInstrumentKWArgs]\") -> None:\n", " \"\"\"\n", " Args:\n", " name: Name to use internally in QCoDeS\n", " address: VISA ressource address\n", - " terminator: read/write terminator for communication\n", " **kwargs: kwargs are forwarded to the base class.\n", " \"\"\"\n", - " super().__init__(name, address, terminator=\"\\n\", **kwargs)\n", + " super().__init__(name, address, **kwargs)\n", "\n", " model = self.ask(\"localnode.model\")\n", "\n", diff --git a/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb b/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb index b8481806c5a..8bd178ac2af 100644 --- a/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb +++ b/docs/examples/writing_drivers/Creating-Simulated-PyVISA-Instruments.ipynb @@ -82,9 +82,10 @@ " QCoDeS driver for the stepped attenuator\n", " Weinschel is formerly known as Aeroflex/Weinschel\n", " \"\"\"\n", + " default_terminator = \"\\r\"\n", "\n", - " def __init__(self, name: str, address: str, terminator='\\r', **kwargs: 'Unpack[VisaInstrumentKWArgs]'):\n", - " super().__init__(name, address, terminator=terminator, **kwargs)\n", + " def __init__(self, name: str, address: str, **kwargs: 'Unpack[VisaInstrumentKWArgs]'):\n", + " super().__init__(name, address, **kwargs)\n", "\n", " self.add_parameter('attenuation', unit='dB',\n", " set_cmd='ATTN ALL {:02.0f}',\n", From 90451da1d36e380907e525ff0c3010948e87fbf2 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 17 May 2024 14:29:12 +0200 Subject: [PATCH 23/23] Add changelog for 6012 --- docs/changes/newsfragments/6012.breaking | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/changes/newsfragments/6012.breaking diff --git a/docs/changes/newsfragments/6012.breaking b/docs/changes/newsfragments/6012.breaking new file mode 100644 index 00000000000..8db0bb5fa7c --- /dev/null +++ b/docs/changes/newsfragments/6012.breaking @@ -0,0 +1,10 @@ +The keyword arguments expected to be passed to ``InstrumentBase`` and ``VisaInstrument`` subclasses are now +documented as TypedDics classes that can be used to type `**kwargs` in the subclass constructors. +See `Creating QCoDeS instrument drivers` for usage examples. + +This also means that the these arguments **must** be passed as keyword arguments, and not as positional arguments. +This specifically includeds passing ``label`` and ``metadata`` to direct subclasses of ``Instrument`` as well as +``terminator`` to subclasses of ``VisaInstrument``. + +All drivers shipping with qcodes for Vendors from A-K have been updated. +The remaining drivers will be updated in a subsequent pull request.