diff --git a/pyproject.toml b/pyproject.toml index 87fbe0b..14ae685 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ select = [ "W", # pycodestyle warnings ] ignore = [ + "RUF006", # asyncio-dangling-task "PERF203", # try-except-in-loop ] diff --git a/src/pydase/data_service/data_service_observer.py b/src/pydase/data_service/data_service_observer.py index afcdc2b..aeabfef 100644 --- a/src/pydase/data_service/data_service_observer.py +++ b/src/pydase/data_service/data_service_observer.py @@ -23,6 +23,13 @@ class DataServiceObserver(PropertyObserver): super().__init__(state_manager.service) def on_change(self, full_access_path: str, value: Any) -> None: + if any( + full_access_path.startswith(changing_attribute) + and full_access_path != changing_attribute + for changing_attribute in self.changing_attributes + ): + return + cached_value_dict = deepcopy( self.state_manager._data_service_cache.get_value_dict_from_cache( full_access_path diff --git a/src/pydase/observer_pattern/observable/observable_object.py b/src/pydase/observer_pattern/observable/observable_object.py index 1f56644..40ef632 100644 --- a/src/pydase/observer_pattern/observable/observable_object.py +++ b/src/pydase/observer_pattern/observable/observable_object.py @@ -148,6 +148,7 @@ class _ObservableList(ObservableObject, list[Any]): self._notify_changed(f"[{key}]", value) def append(self, __object: Any) -> None: + self._notify_change_start("") self._initialise_new_objects(f"[{len(self)}]", __object) super().append(__object) self._notify_changed("", self) diff --git a/src/pydase/observer_pattern/observer/observer.py b/src/pydase/observer_pattern/observer/observer.py index b36dc6b..a73312c 100644 --- a/src/pydase/observer_pattern/observer/observer.py +++ b/src/pydase/observer_pattern/observer/observer.py @@ -14,11 +14,11 @@ class Observer(ABC): self.changing_attributes: list[str] = [] def _notify_changed(self, changed_attribute: str, value: Any) -> None: + self.on_change(full_access_path=changed_attribute, value=value) + if changed_attribute in self.changing_attributes: self.changing_attributes.remove(changed_attribute) - self.on_change(full_access_path=changed_attribute, value=value) - def _notify_change_start(self, changing_attribute: str) -> None: self.changing_attributes.append(changing_attribute) self.on_change_start(changing_attribute) diff --git a/tests/data_service/test_data_service_observer.py b/tests/data_service/test_data_service_observer.py index 875d239..9e487fb 100644 --- a/tests/data_service/test_data_service_observer.py +++ b/tests/data_service/test_data_service_observer.py @@ -94,3 +94,31 @@ def test_protected_or_private_change_logs(caplog: pytest.LogCaptureFixture) -> N service.subclass._name = "Hello" assert "'subclass._name' changed to 'Hello'" not in caplog.text + + +def test_dynamic_list_entry_with_property(caplog: pytest.LogCaptureFixture) -> None: + class PropertyClass(pydase.DataService): + _name = "Hello" + + @property + def name(self) -> str: + """The name property.""" + return self._name + + class MyService(pydase.DataService): + def __init__(self) -> None: + super().__init__() + self.list_attr = [] + + def toggle_high_voltage(self) -> None: + self.list_attr = [] + self.list_attr.append(PropertyClass()) + self.list_attr[0]._name = "Hoooo" + + service = MyService() + state_manager = StateManager(service) + DataServiceObserver(state_manager) + service.toggle_high_voltage() + + assert "'list_attr[0].name' changed to 'Hello'" not in caplog.text + assert "'list_attr[0].name' changed to 'Hoooo'" in caplog.text