Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to find parameter chain links by type #6599

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changes/newsfragments/6599.new
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a new feature to find links in a parameter chain by parameter type
8 changes: 8 additions & 0 deletions src/qcodes/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
from .infer import (
InferAttrs,
InferError,
get_chain_links_of_type,
get_instrument_from_param,
get_parameter_chain,
get_root_parameter,
get_sole_chain_link_of_type,
infer_channel,
infer_instrument,
infer_instrument_module,
Expand All @@ -21,7 +25,11 @@
"DriverTestCase",
"InferAttrs",
"InferError",
"get_chain_links_of_type",
"get_instrument_from_param",
"get_parameter_chain",
"get_root_parameter",
"get_sole_chain_link_of_type",
"infer_channel",
"infer_instrument",
"infer_instrument_module",
Expand Down
37 changes: 35 additions & 2 deletions src/qcodes/extensions/infer.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING, ClassVar
from typing import TYPE_CHECKING, ClassVar, TypeVar, cast

from qcodes.instrument import Instrument, InstrumentBase, InstrumentModule
from qcodes.parameters import DelegateParameter, Parameter
from qcodes.parameters import DelegateParameter, Parameter, ParameterBase

if TYPE_CHECKING:
from collections.abc import Iterable

DOES_NOT_EXIST = "Does not exist"

C = TypeVar("C", bound=ParameterBase)
picarro-yren marked this conversation as resolved.
Show resolved Hide resolved


class InferError(AttributeError): ...

Expand Down Expand Up @@ -220,3 +222,34 @@
return set.union(set((alt_source_attrs,)), set(InferAttrs.known_attrs()))
else:
return set.union(set(alt_source_attrs), set(InferAttrs.known_attrs()))


def get_chain_links_of_type(
link_param_type: type[C] | tuple[type[C], ...], parameter: Parameter
) -> tuple[C, ...]:
"""Gets all parameters in a chain of linked parameters that match a given type"""
chain_links: list[C] = [
cast(C, param)
for param in get_parameter_chain(parameter)
if isinstance(param, link_param_type)
]
return tuple(chain_links)


def get_sole_chain_link_of_type(
link_param_type: type[C] | tuple[type[C], ...], parameter: Parameter
) -> C:
"""Gets the one parameter in a chain of linked parameters that matches a given type"""

chain_links = get_chain_links_of_type(
link_param_type=link_param_type, parameter=parameter
)
if len(chain_links) != 1:
if isinstance(link_param_type, type):
error_msg_1 = f"Expected only a single chain link of type {link_param_type.__name__} but found {len(chain_links)}: \n"
elif isinstance(link_param_type, tuple):
type_strs = [link_type.__name__ for link_type in link_param_type]
error_msg_1 = f"Expected only a single chain link of types {type_strs} but found {len(chain_links)}: \n"

Check warning on line 252 in src/qcodes/extensions/infer.py

View check run for this annotation

Codecov / codecov/patch

src/qcodes/extensions/infer.py#L250-L252

Added lines #L250 - L252 were not covered by tests

raise ValueError(error_msg_1 + f"{[link.name for link in chain_links]}")
return chain_links[0]

Check warning on line 255 in src/qcodes/extensions/infer.py

View check run for this annotation

Codecov / codecov/patch

src/qcodes/extensions/infer.py#L255

Added line #L255 was not covered by tests
28 changes: 28 additions & 0 deletions tests/extensions/test_infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
InferAttrs,
InferError,
_merge_user_and_class_attrs,
get_chain_links_of_type,
get_parameter_chain,
get_root_parameter,
get_sole_chain_link_of_type,
infer_channel,
infer_instrument,
)
Expand Down Expand Up @@ -280,3 +282,29 @@ def test_get_root_parameter_with_loops(good_inst_delegates):
with pytest.raises(InferError) as exc_info:
get_root_parameter(good_inst_del_2, "linked_parameter")
assert "generated a loop of linking parameters" in str(exc_info.value)


def test_chain_links_of_type():
root_param = ManualParameter("root", initial_value=0)
user_link = UserLinkingParameter("user_link", linked_parameter=root_param)
delegate_link = DelegateParameter("delegate_link", source=user_link)
user_link_2 = UserLinkingParameter("user_link_2", linked_parameter=delegate_link)

InferAttrs.add("linked_parameter")
user_links = get_chain_links_of_type(UserLinkingParameter, user_link_2)
assert set(user_links) == set([user_link, user_link_2])


def test_sole_chain_link_of_type():
root_param = ManualParameter("root", initial_value=0)
user_link = UserLinkingParameter("user_link", linked_parameter=root_param)
delegate_link = DelegateParameter("delegate_link", source=user_link)
user_link_2 = UserLinkingParameter("user_link_2", linked_parameter=delegate_link)

InferAttrs.add("linked_parameter")
user_links = get_chain_links_of_type(UserLinkingParameter, delegate_link)
assert set(user_links) == set([user_link])

with pytest.raises(ValueError) as exc_info:
user_links = get_sole_chain_link_of_type(UserLinkingParameter, user_link_2)
assert "Expected only a single chain link of type" in str(exc_info.value)
Loading