From e90967eb036e0d22cd2e486afc244a97b1db9d1b Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 21 Mar 2024 20:23:27 +0100 Subject: [PATCH 1/3] fix bug with adding __class__ to list --- quam/core/quam_classes.py | 3 ++- quam/core/quam_instantiation.py | 6 +++++- .../instantiation/test_instantiate_explicit_class.py | 12 ++++++++++++ tests/quam_base/test_quam_list.py | 5 +++++ tests/quam_base/test_to_dict.py | 9 +++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/quam/core/quam_classes.py b/quam/core/quam_classes.py index ab6dfb93..20a55013 100644 --- a/quam/core/quam_classes.py +++ b/quam/core/quam_classes.py @@ -417,7 +417,8 @@ def to_dict( follow_references=follow_references, include_defaults=include_defaults, ) - if not self._val_matches_attr_annotation(attr, val): + val_is_list = isinstance(val, (list, UserList)) + if not self._val_matches_attr_annotation(attr, val) and not val_is_list: quam_dict[attr]["__class__"] = get_full_class_path(val) else: quam_dict[attr] = val diff --git a/quam/core/quam_instantiation.py b/quam/core/quam_instantiation.py index 28a8bd12..75dd9eb5 100644 --- a/quam/core/quam_instantiation.py +++ b/quam/core/quam_instantiation.py @@ -205,7 +205,11 @@ def instantiate_attr( ) if typing.get_origin(expected_type) == dict: expected_type = dict - elif isinstance(expected_type, list) or typing.get_origin(expected_type) == list: + elif ( + isinstance(expected_type, list) + or typing.get_origin(expected_type) == list + or isinstance(attr_val, list) + ): instantiated_attr = instantiate_attrs_from_list( attr_list=attr_val, required_type=expected_type, diff --git a/tests/instantiation/test_instantiate_explicit_class.py b/tests/instantiation/test_instantiate_explicit_class.py index a2cbe421..49fa31f6 100644 --- a/tests/instantiation/test_instantiate_explicit_class.py +++ b/tests/instantiation/test_instantiate_explicit_class.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import List, Union from quam.core import * from quam.core.quam_instantiation import * from quam.utils import get_full_class_path @@ -25,3 +26,14 @@ def test_instantiate_from_class(): assert type(loaded_explicit_component) == type(loaded_explicit_component2) assert isinstance(loaded_explicit_component, QuamComponentTest) + + +def test_instantiate_nondefault_list_from_dict(): + @quam_dataclass + class QuamBasicComponent(QuamComponent): + l: Union[int, List[int]] = 42 + + quam_component = QuamBasicComponent(l=[1, 2, 3]) + + d = quam_component.to_dict() + instantiate_quam_class(QuamBasicComponent, d) diff --git a/tests/quam_base/test_quam_list.py b/tests/quam_base/test_quam_list.py index a5a23a14..d4fbf427 100644 --- a/tests/quam_base/test_quam_list.py +++ b/tests/quam_base/test_quam_list.py @@ -74,3 +74,8 @@ def test_list_get_attrs_error(): quam_list = QuamList() with pytest.raises(NotImplementedError): quam_list.get_attrs() + + +def test_list_to_dict(): + quam_list = QuamList([1, 2, 3]) + assert quam_list.to_dict() == [1, 2, 3] \ No newline at end of file diff --git a/tests/quam_base/test_to_dict.py b/tests/quam_base/test_to_dict.py index e882ee7f..5d3db543 100644 --- a/tests/quam_base/test_to_dict.py +++ b/tests/quam_base/test_to_dict.py @@ -142,3 +142,12 @@ class QuamBasicComponent(QuamComponent): quam_component = QuamBasicComponent(l=[1, 2, 3]) assert quam_component.to_dict() == {"l": [1, 2, 3]} + + +def test_list_to_dict_nondefault(): + @quam_dataclass + class QuamBasicComponent(QuamComponent): + l: int = 42 + + quam_component = QuamBasicComponent(l=[1, 2, 3]) + assert quam_component.to_dict() == {"l": [1, 2, 3]} From 62a9f217f862982c380c3a88c58cd7084bc38d91 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 21 Mar 2024 20:50:04 +0100 Subject: [PATCH 2/3] fix instantiation in rare cases --- .gitignore | 1 + quam/core/quam_instantiation.py | 31 ++++++++++++------- .../test_instantiate_explicit_class.py | 23 ++++++++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index b8f61bbf..bbcdf0ef 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ additional .idea build site +calibration_db.json dependency_links.txt PKG-INFO diff --git a/quam/core/quam_instantiation.py b/quam/core/quam_instantiation.py index 75dd9eb5..613c2641 100644 --- a/quam/core/quam_instantiation.py +++ b/quam/core/quam_instantiation.py @@ -107,7 +107,15 @@ def instantiate_attrs_from_list( instantiated_attr_list = [] for k, attr_val in enumerate(attr_list): - if not required_subtype: + if isinstance(attr_val, dict) and "__class__" in attr_val: + instantiated_attr = instantiate_quam_class( + get_class_from_path(attr_val["__class__"]), + attr_val, + fix_attrs=fix_attrs, + validate_type=validate_type, + str_repr=f"{str_repr}[{k}]", + ) + elif not required_subtype: instantiated_attr_list.append(attr_val) continue elif typing.get_origin(required_subtype) == list: @@ -136,8 +144,8 @@ def instantiate_attrs_from_list( else: instantiated_attr = attr_val # Add custom __class__ QuamComponent logic here - - validate_obj_type(instantiated_attr, required_subtype, str_repr=str_repr) + if required_subtype: + validate_obj_type(instantiated_attr, required_subtype, str_repr=str_repr) instantiated_attr_list.append(instantiated_attr) @@ -195,6 +203,14 @@ def instantiate_attr( validate_type=validate_type, str_repr=str_repr, ) + elif isinstance(attr_val, dict) and "__class__" in attr_val: + instantiated_attr = instantiate_quam_class( + quam_class=expected_type, + contents=attr_val, + fix_attrs=fix_attrs, + validate_type=validate_type, + str_repr=str_repr, + ) elif isinstance(expected_type, dict) or typing.get_origin(expected_type) == dict: instantiated_attr = instantiate_attrs_from_dict( attr_dict=attr_val, @@ -220,16 +236,7 @@ def instantiate_attr( if typing.get_origin(expected_type) == list: expected_type = list elif typing.get_origin(expected_type) == typing.Union: - if not all( - t in [str, int, float, bool, type(None)] - for t in typing.get_args(expected_type) - ): - raise TypeError( - "Currently only Union[str, int, float, bool] is supported, whereas " - f"{expected_type} was found in {str_repr}" - ) instantiated_attr = attr_val - elif typing.get_origin(expected_type) == tuple: if isinstance(attr_val, list): attr_val = tuple(attr_val) diff --git a/tests/instantiation/test_instantiate_explicit_class.py b/tests/instantiation/test_instantiate_explicit_class.py index 49fa31f6..1092a9ad 100644 --- a/tests/instantiation/test_instantiate_explicit_class.py +++ b/tests/instantiation/test_instantiate_explicit_class.py @@ -37,3 +37,26 @@ class QuamBasicComponent(QuamComponent): d = quam_component.to_dict() instantiate_quam_class(QuamBasicComponent, d) + + +@quam_dataclass +class QuamBasicComponent(QuamComponent): + int_val: int = 42 + + +@quam_dataclass +class QuamOuterComponent(QuamComponent): + list_basic_components: Union[int, List[QuamBasicComponent]] + + +def test_instantiate_explicit_class_with_union_type(): + quam_component = QuamOuterComponent( + list_basic_components=[QuamBasicComponent(int_val=42)] + ) + + d = quam_component.to_dict() + component = instantiate_quam_class(QuamOuterComponent, d) + + assert isinstance(component, QuamOuterComponent) + assert isinstance(component.list_basic_components[0], QuamBasicComponent) + assert component.to_dict() == quam_component.to_dict() From 67375e37ad5a056b0d3a14ba7ad92213ad78d83d Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Fri, 22 Mar 2024 12:01:43 +0100 Subject: [PATCH 3/3] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbf8a4ed..0e057c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## [Unreleased] ### Fixed - Switched channel `RF_inputs` and `RF_outputs` for Octave +- Loading QuAM components when the expected type is a union or the actual type is a list + no longer raises an error ## [0.3.0]