Skip to content

Commit

Permalink
Lookup component from channeltuple
Browse files Browse the repository at this point in the history
This enables a component to be looked up from
a channellist/tuple by its full_name
even if the component has not been added as
an individual component but only as a member
of a channeltuple/list.
  • Loading branch information
jenshnielsen committed Apr 26, 2024
1 parent eb18eeb commit 0b53bcd
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/changes/newsfragments/6009.improved
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Station.get_component and Instrument.get_component has gained the ability to lookup a component
on an instrument that is only added as a member of a chanellist/tuple and not added as an individual component.
25 changes: 22 additions & 3 deletions src/qcodes/instrument/instrument_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,27 @@ def get_component(self, full_name: str) -> MetadatableWithName:
def _get_component_by_name(
self, potential_top_level_name: str, remaining_name_parts: list[str]
) -> MetadatableWithName:

log.debug(
"trying to find component %s on %s, remaining %s",
potential_top_level_name,
self.full_name,
remaining_name_parts,
)
component: MetadatableWithName | None = None

sub_component_name_map = {
sub_component.short_name: sub_component
for sub_component in self.submodules.values()
}

channel_name_map: dict[str, InstrumentModule] = {}
for channel_list in self._channel_lists.values():
local_channels_name_map: dict[str, InstrumentModule] = {
channel.short_name: channel for channel in channel_list
}
channel_name_map.update(local_channels_name_map.items())

if potential_top_level_name in self.parameters:
component = self.parameters[potential_top_level_name]
elif potential_top_level_name in self.functions:
Expand All @@ -283,13 +297,18 @@ def _get_component_by_name(
remaining_name = "_".join(remaining_name_parts)
component = component.get_component(remaining_name)
remaining_name_parts = []
elif potential_top_level_name in channel_name_map:
component = channel_name_map[potential_top_level_name]
if len(remaining_name_parts) > 0:
remaining_name_parts.reverse()
remaining_name = "_".join(remaining_name_parts)
component = component.get_component(remaining_name)
remaining_name_parts = []

if component is not None:
if len(remaining_name_parts) == 0:
return component

remaining_name_parts.reverse()

if len(remaining_name_parts) == 0:
raise KeyError(
f"Found component {self.full_name} but could not "
Expand All @@ -299,7 +318,7 @@ def _get_component_by_name(
new_potential_top_level_name = (
f"{potential_top_level_name}_{remaining_name_parts.pop()}"
)
remaining_name_parts.reverse()

component = self._get_component_by_name(
new_potential_top_level_name, remaining_name_parts
)
Expand Down
21 changes: 20 additions & 1 deletion src/qcodes/instrument_drivers/mock_instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ def turn_on(self) -> None:
pass


class DummyChannelInstrument(Instrument):
class DummyChannelInstrument(DummyBase):
"""
Dummy instrument with channels
"""
Expand All @@ -474,6 +474,25 @@ def __init__(
self.add_submodule("channels", channels.to_channel_tuple())


class DummyChannelOnlyInstrument(DummyBase):
"""
Dummy instrument with channels that have not been added as individual submodules.
Also use module names with _ in them to check that we can handle that.
"""

def __init__(self, name: str, **kwargs: Any):
super().__init__(name, **kwargs)

channels = ChannelList(self, "Temp_Sensors", DummyChannel)
channel_ids: Sequence[str] = ("A_a", "B_b", "C_c", "D_d", "E_e", "F_f")
channel_names = tuple(f"Chan{chan_name}" for chan_name in channel_ids)

for chan_name, chan_id in zip(channel_names, channel_ids):
channel = DummyChannel(self, chan_name, chan_id)
channels.append(channel)
self.add_submodule("channels", channels.to_channel_tuple())


class MultiGetter(MultiParameter):
"""
Test parameters with complicated return values
Expand Down
11 changes: 10 additions & 1 deletion tests/test_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from qcodes.instrument import Instrument
from qcodes.instrument_drivers.mock_instruments import (
DummyChannelInstrument,
DummyChannelOnlyInstrument,
DummyInstrument,
)
from qcodes.monitor import Monitor
Expand Down Expand Up @@ -891,15 +892,23 @@ def test_station_config_created_with_multiple_config_files() -> None:

def test_get_component_by_name() -> None:
instr = DummyChannelInstrument(name="dummy")
instr2 = DummyChannelOnlyInstrument(name="some_other_dummy")
param = Parameter(name="param", set_cmd=None, get_cmd=None)
station = Station(instr, param)
station = Station(instr, instr2, param)

assert station.get_component("dummy") is instr
assert station.get_component("dummy_A") is instr.A
assert station.get_component("dummy_ChanA") is instr.A
assert station.get_component("dummy_A_temperature") is instr.A.temperature
assert station.get_component("dummy_ChanA_temperature") is instr.A.temperature

assert station.get_component("some_other_dummy") is instr2
assert station.get_component("some_other_dummy_ChanA_a") is instr2.channels[0]
assert (
station.get_component("some_other_dummy_ChanA_a_temperature")
is instr2.channels[0].temperature
)

assert station.get_component("param") is param


Expand Down

0 comments on commit 0b53bcd

Please sign in to comment.