From c4e7fe66a801a6e7fb613310ee77a31c7488cc46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Thu, 22 May 2025 15:34:09 +0200 Subject: [PATCH 1/2] fix: properly checking is attribute is nested Properties whose names start with a dependency's name (e.g., my_int -> my_int_2) were incorrectly skipped during change notification. This fixes it by checking if the changing properties start with the full_access_path start followed by either "." or "[". --- .../data_service/data_service_observer.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/pydase/data_service/data_service_observer.py b/src/pydase/data_service/data_service_observer.py index ec686c7..95b4df8 100644 --- a/src/pydase/data_service/data_service_observer.py +++ b/src/pydase/data_service/data_service_observer.py @@ -20,6 +20,19 @@ from pydase.utils.serialization.types import SerializedObject logger = logging.getLogger(__name__) +def _is_nested_attribute(full_access_path: str, changing_attributes: list[str]) -> bool: + """Return True if the full_access_path is a nested attribute of any + changing_attribute.""" + + return any( + ( + full_access_path.startswith((f"{attr}.", f"{attr}[")) + and full_access_path != attr + ) + for attr in changing_attributes + ) + + class DataServiceObserver(PropertyObserver): def __init__(self, state_manager: StateManager) -> None: self.state_manager = state_manager @@ -29,11 +42,7 @@ 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 - ): + if _is_nested_attribute(full_access_path, self.changing_attributes): return cached_value_dict: SerializedObject From d0153331236218ee0f2742d2f8a58bbd29b96d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Thu, 22 May 2025 15:34:42 +0200 Subject: [PATCH 2/2] tests: property starting with dependency name --- .../test_data_service_observer.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/data_service/test_data_service_observer.py b/tests/data_service/test_data_service_observer.py index e6e9cb1..03c5bed 100644 --- a/tests/data_service/test_data_service_observer.py +++ b/tests/data_service/test_data_service_observer.py @@ -262,3 +262,22 @@ def test_dependency_as_function_argument(caplog: pytest.LogCaptureFixture) -> No service_instance.some_int = 1337 assert "'other_int' changed to '1338'" in caplog.text + + +def test_property_starting_with_dependency_name( + caplog: pytest.LogCaptureFixture, +) -> None: + class MyObservable(pydase.DataService): + my_int = 0 + + @property + def my_int_2(self) -> int: + return self.my_int + 1 + + service_instance = MyObservable() + state_manager = StateManager(service=service_instance) + DataServiceObserver(state_manager) + + service_instance.my_int = 1337 + + assert "'my_int_2' changed to '1338'" in caplog.text