Skip to content

Commit 8817ba7

Browse files
authored
Merge pull request #7659 from jenshnielsen/generic_parent
Make InstrumentModule/Channel take a generic instrument argument
2 parents aaf055b + 3c9010c commit 8817ba7

7 files changed

Lines changed: 53 additions & 37 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The ``InstrumentModule`` and its alias ``InstrumentChannel`` now take an optional generic argument allowing you to specify the type of the parent instrument for type checking.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The ``KeysightE4980A`` driver now names the ``correction`` submodule correctly as ``correction`` reflecting the public attribute to access the module.
2+
This also means that in the snapshot ``correction`` is used as the module name rather than ``_correction``

src/qcodes/instrument/channel.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import sys
66
import warnings
77
from collections.abc import Callable, Iterable, Iterator, MutableSequence, Sequence
8-
from typing import TYPE_CHECKING, Any, TypeVar, cast, overload
8+
from typing import TYPE_CHECKING, Any, Generic, cast, overload
9+
10+
from typing_extensions import TypeVar
911

1012
from qcodes.metadatable import MetadatableWithName
1113
from qcodes.parameters import (
@@ -28,7 +30,12 @@
2830
from .instrument_base import InstrumentBaseKWArgs
2931

3032

31-
class InstrumentModule(InstrumentBase):
33+
_TIB_co = TypeVar(
34+
"_TIB_co", bound="InstrumentBase", default=InstrumentBase, covariant=True
35+
)
36+
37+
38+
class InstrumentModule(InstrumentBase, Generic[_TIB_co]):
3239
"""
3340
Base class for a module in an instrument.
3441
This could be in the form of a channel (e.g. something that
@@ -45,7 +52,7 @@ class InstrumentModule(InstrumentBase):
4552
"""
4653

4754
def __init__(
48-
self, parent: InstrumentBase, name: str, **kwargs: Unpack[InstrumentBaseKWArgs]
55+
self, parent: _TIB_co, name: str, **kwargs: Unpack[InstrumentBaseKWArgs]
4956
) -> None:
5057
# need to specify parent before `super().__init__` so that the right
5158
# `full_name` is available in that scope. `full_name` is used for
@@ -76,7 +83,7 @@ def ask_raw(self, cmd: str) -> str:
7683
return self._parent.ask_raw(cmd)
7784

7885
@property
79-
def parent(self) -> InstrumentBase:
86+
def parent(self) -> _TIB_co:
8087
return self._parent
8188

8289
@property
@@ -90,7 +97,7 @@ def name_parts(self) -> list[str]:
9097
return name_parts
9198

9299

93-
class InstrumentChannel(InstrumentModule):
100+
class InstrumentChannel(InstrumentModule[_TIB_co], Generic[_TIB_co]):
94101
pass
95102

96103

src/qcodes/instrument_drivers/Keithley/Keithley_2450.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class _FunctionMode(TypedDict):
213213
range_vals: Numbers
214214

215215

216-
class Keithley2450Sense(InstrumentChannel):
216+
class Keithley2450Sense(InstrumentChannel["Keithley2450"]):
217217
"""
218218
The sense module of the Keithley 2450 SMU.
219219
@@ -340,7 +340,7 @@ def _measure(self) -> float | str:
340340
return float(self.ask(f":MEASure? '{buffer_name}'"))
341341

342342
def _measure_sweep(self) -> npt.NDArray:
343-
source = cast("Keithley2450Source", self.parent.source)
343+
source = self.parent.source
344344
source.sweep_start()
345345
buffer_name = self.parent.buffer_name()
346346
buffer = cast(

src/qcodes/instrument_drivers/Keysight/keysight_e4980a.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, Any, cast
1+
from typing import TYPE_CHECKING, Any
22

33
from packaging import version
44
from pyvisa.errors import VisaIOError
@@ -371,15 +371,14 @@ def __init__(
371371
)
372372
"""This parameter tracks the signal mode which is being set."""
373373

374-
self.add_submodule("_correction", KeysightE4980ACorrection(self, "correction"))
374+
self.correction: KeysightE4980ACorrection = self.add_submodule(
375+
"correction", KeysightE4980ACorrection(self, "correction")
376+
)
377+
"""Correction submodule"""
378+
375379
self._set_signal_mode_on_driver_initialization()
376380
self.connect_message()
377381

378-
@property
379-
def correction(self) -> KeysightE4980ACorrection:
380-
submodule = self.submodules["_correction"]
381-
return cast("KeysightE4980ACorrection", submodule)
382-
383382
@property
384383
def measure_impedance(self) -> KeysightE4980AMeasurementPair:
385384
return self._get_complex_impedance()

src/qcodes/instrument_drivers/Lakeshore/Lakeshore_model_325.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
SupportsBytes,
88
SupportsIndex,
99
TextIO,
10-
cast,
1110
)
1211

1312
from qcodes.instrument import (
@@ -303,7 +302,7 @@ def set_data(
303302
self.write(cmd_str)
304303

305304

306-
class LakeshoreModel325Sensor(InstrumentChannel):
305+
class LakeshoreModel325Sensor(InstrumentChannel["LakeshoreModel325"]):
307306
"""
308307
InstrumentChannel for a single sensor of a Lakeshore Model 325.
309308
@@ -396,8 +395,7 @@ def decode_sensor_status(sum_of_codes: int) -> str:
396395

397396
@property
398397
def curve(self) -> LakeshoreModel325Curve:
399-
parent = cast("LakeshoreModel325", self.parent)
400-
return LakeshoreModel325Curve(parent, self.curve_index())
398+
return LakeshoreModel325Curve(self.parent, self.curve_index())
401399

402400

403401
class LakeshoreModel325Heater(InstrumentChannel):
@@ -582,7 +580,10 @@ def __init__(
582580
super().__init__(name, address, **kwargs)
583581

584582
sensors = ChannelList(
585-
self, "sensor", LakeshoreModel325Sensor, snapshotable=False
583+
self,
584+
"sensor",
585+
LakeshoreModel325Sensor,
586+
snapshotable=False,
586587
)
587588

588589
self.sensor_A: LakeshoreModel325Sensor = self.add_submodule(

src/qcodes/instrument_drivers/rigol/Rigol_DG1062.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import logging
22
from functools import partial
3-
from typing import TYPE_CHECKING, Any, ClassVar, cast
3+
from typing import TYPE_CHECKING, Any, ClassVar
44

55
from qcodes import validators as vals
66
from qcodes.instrument import (
7-
ChannelList,
87
InstrumentBaseKWArgs,
98
InstrumentChannel,
109
VisaInstrument,
1110
VisaInstrumentKWArgs,
1211
)
12+
from qcodes.instrument.channel import ChannelTuple
1313
from qcodes.utils import partial_with_docstring
1414

1515
if TYPE_CHECKING:
@@ -20,7 +20,7 @@
2020
log = logging.getLogger(__name__)
2121

2222

23-
class RigolDG1062Burst(InstrumentChannel):
23+
class RigolDG1062Burst(InstrumentChannel["RigolDG1062"]):
2424
"""
2525
Burst commands for the DG1062. We make a separate channel for these to
2626
group burst commands together.
@@ -129,7 +129,7 @@ def trigger(self) -> None:
129129
self.parent.write_raw(f":SOUR{self.channel}:BURS:TRIG")
130130

131131

132-
class RigolDG1062Channel(InstrumentChannel):
132+
class RigolDG1062Channel(InstrumentChannel["RigolDG1062"]):
133133
min_impedance = 1
134134
max_impedance = 10000
135135

@@ -261,10 +261,9 @@ def __init__(
261261
For other waveforms it will give the user an error
262262
"""
263263

264-
burst = RigolDG1062Burst(
265-
cast("RigolDG1062", self.parent), "burst", self.channel
266-
)
267-
self.add_submodule("burst", burst)
264+
burst = RigolDG1062Burst(self.parent, "burst", self.channel)
265+
self.burst: RigolDG1062Burst = self.add_submodule("burst", burst)
266+
""""Burst submodule"""
268267

269268
# We want to be able to do the following:
270269
# >>> help(gd.channels[0].sin)
@@ -371,7 +370,7 @@ def _set_waveform_params(self, **params_dict: float) -> None:
371370
string += ",".join(values)
372371
self.parent.write_raw(string)
373372

374-
def _get_duty_cycle(self) -> float:
373+
def _get_duty_cycle(self) -> str:
375374
"""
376375
Reads the duty cycle after checking waveform
377376
"""
@@ -415,13 +414,20 @@ def __init__(
415414
):
416415
super().__init__(name, address, **kwargs)
417416

418-
channels = ChannelList(self, "channel", RigolDG1062Channel, snapshotable=False)
419-
420-
for ch_num in [1, 2]:
421-
ch_name = f"ch{ch_num}"
422-
channel = RigolDG1062Channel(self, ch_name, ch_num)
423-
channels.append(channel)
424-
self.add_submodule(ch_name, channel)
425-
426-
self.add_submodule("channels", channels.to_channel_tuple())
417+
self.ch1 = self.add_submodule("ch1", RigolDG1062Channel(self, "ch1", 1))
418+
"""Channel 1 submodule"""
419+
self.ch2 = self.add_submodule("ch2", RigolDG1062Channel(self, "ch2", 2))
420+
"""Channel 2 submodule"""
421+
422+
self.channels: ChannelTuple[RigolDG1062Channel] = self.add_submodule(
423+
"channels",
424+
ChannelTuple(
425+
self,
426+
"channel",
427+
chan_type=RigolDG1062Channel,
428+
chan_list=(self.ch1, self.ch2),
429+
snapshotable=False,
430+
),
431+
)
432+
"""Tuple of channels"""
427433
self.connect_message()

0 commit comments

Comments
 (0)