Skip to content

Commit 10fe61e

Browse files
authored
Merge pull request #4257 from jenshnielsen/validate_full_name
Let instrument names be less restrictive.
2 parents 3f6274c + e7f15b0 commit 10fe61e

File tree

6 files changed

+55
-13
lines changed

6 files changed

+55
-13
lines changed
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Restrictions on instrument names are now less strict than in ``0.34.0``. Submodules are allowed
2+
to have names that are not valid identifier as long as the full name is an valid identifier.

qcodes/instrument/base.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class InstrumentBase(Metadatable, DelegateAttributes):
5252
"""
5353

5454
def __init__(self, name: str, metadata: Optional[Mapping[Any, Any]] = None) -> None:
55-
name = self._is_valid_identifier(name)
56-
self._short_name = str(name)
55+
name = self._replace_hyphen(name)
56+
self._short_name = name
57+
self._is_valid_identifier(self.full_name)
5758

5859
self.parameters: Dict[str, _BaseParameter] = {}
5960
"""
@@ -409,13 +410,17 @@ def short_name(self) -> str:
409410
return self._short_name
410411

411412
@staticmethod
412-
def _is_valid_identifier(name: str) -> str:
413+
def _is_valid_identifier(name: str) -> None:
413414
"""Check whether given name is a valid instrument identifier."""
414-
new_name = name.replace("-", "_")
415+
if not name.isidentifier():
416+
raise ValueError(f"{name} invalid instrument identifier")
417+
418+
@staticmethod
419+
def _replace_hyphen(name: str) -> str:
420+
"""Replace - in name with _ and warn if any is found."""
421+
new_name = str(name).replace("-", "_")
415422
if name != new_name:
416423
warnings.warn(f"Changed {name} to {new_name} for instrument identifier")
417-
if not new_name.isidentifier():
418-
raise ValueError(f"{new_name} invalid instrument identifier")
419424

420425
return new_name
421426

qcodes/instrument/channel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def root_instrument(self) -> InstrumentBase:
8989

9090
@property
9191
def name_parts(self) -> List[str]:
92-
name_parts = self._parent.name_parts
92+
name_parts = list(self._parent.name_parts)
9393
name_parts.append(self.short_name)
9494
return name_parts
9595

qcodes/tests/drivers/keysight_b1500/b1500_driver_tests/conftest.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import MagicMock
1+
from unittest.mock import MagicMock, PropertyMock
22

33
import pytest
44
from pyvisa import VisaIOError
@@ -34,4 +34,12 @@ def _make_b1500(request):
3434

3535
@pytest.fixture(name="mainframe")
3636
def _make_mainframe():
37-
yield MagicMock()
37+
PropertyMock()
38+
mainframe = MagicMock()
39+
name_parts = PropertyMock(return_value=["mainframe"])
40+
type(mainframe).name_parts = name_parts
41+
short_name = PropertyMock(return_value="short_name")
42+
type(mainframe).short_name = short_name
43+
full_name = PropertyMock(return_value="mainframe")
44+
type(mainframe).full_name = full_name
45+
yield mainframe

qcodes/tests/instrument_mocks.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,20 @@ class DummyChannelInstrument(Instrument):
424424
Dummy instrument with channels
425425
"""
426426

427-
def __init__(self, name, **kwargs):
427+
def __init__(self, name, channel_names=None, **kwargs):
428428
super().__init__(name, **kwargs)
429429

430430
channels = ChannelList(self, "TempSensors", DummyChannel, snapshotable=False)
431-
for chan_name in ('A', 'B', 'C', 'D', 'E', 'F'):
432-
channel = DummyChannel(self, f'Chan{chan_name}', chan_name)
431+
432+
if channel_names is None:
433+
channel_ids = ("A", "B", "C", "D", "E", "F")
434+
channel_names = tuple(f"Chan{chan_name}" for chan_name in channel_ids)
435+
else:
436+
channel_ids = channel_names
437+
for chan_name, chan_id in zip(channel_names, channel_ids):
438+
channel = DummyChannel(self, chan_name, chan_id)
433439
channels.append(channel)
434-
self.add_submodule(chan_name, channel)
440+
self.add_submodule(chan_id, channel)
435441
self.add_submodule("channels", channels.to_channel_tuple())
436442

437443

qcodes/tests/test_instrument.py

+21
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ def test_instrument_on_invalid_identifier(close_before_and_after):
9696
assert DummyInstrument.instances() == []
9797
assert Instrument._all_instruments == {}
9898

99+
100+
def test_instrument_warns_on_hyphen_in_name(close_before_and_after):
99101
# Check if warning is raised and name is valid
100102
# identifier when dashes '-' are converted to underscores '_'
101103
with pytest.warns(
@@ -109,6 +111,25 @@ def test_instrument_on_invalid_identifier(close_before_and_after):
109111
assert Instrument._all_instruments != {}
110112

111113

114+
def test_instrument_allows_channel_name_starting_with_number(close_before_and_after):
115+
instr = DummyChannelInstrument(name="foo", channel_names=["1", "2", "3"])
116+
117+
for chan in instr.channels:
118+
assert chan.short_name.isidentifier() is False
119+
assert chan.full_name.isidentifier() is True
120+
assert Instrument.instances() == []
121+
assert DummyChannelInstrument.instances() == [instr]
122+
assert Instrument._all_instruments != {}
123+
124+
125+
def test_instrument_channel_name_raise_on_invalid(close_before_and_after):
126+
with pytest.raises(ValueError, match="foo_☃ invalid instrument identifier"):
127+
DummyChannelInstrument(name="foo", channel_names=["☃"])
128+
assert Instrument.instances() == []
129+
assert DummyChannelInstrument.instances() == []
130+
assert Instrument._all_instruments == {}
131+
132+
112133
def test_instrument_retry_with_same_name(close_before_and_after):
113134
with pytest.raises(RuntimeError, match="Failed to create instrument"):
114135
DummyFailingInstrument(name="failinginstrument")

0 commit comments

Comments
 (0)