From 3858c7efc2226a5e1382b46bef367727c3ddd1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Wed, 2 Aug 2023 12:06:19 +0200 Subject: [PATCH] feat: adding notification callback functionality to DataService --- .../data_service/data_service.py | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/pyDataInterface/data_service/data_service.py b/src/pyDataInterface/data_service/data_service.py index 3b4813c..b1c20ed 100644 --- a/src/pyDataInterface/data_service/data_service.py +++ b/src/pyDataInterface/data_service/data_service.py @@ -24,6 +24,7 @@ class DataService(rpyc.Service): be tracked consistently. The keys of the dictionary are the ids of the original lists, and the values are the DataServiceList instances that wrap these lists. """ + _notification_callbacks: list[Callable[[str, str, Any], Any]] = [] def __init__(self) -> None: # Keep track of the root object. This helps to filter the emission of @@ -40,11 +41,7 @@ class DataService(rpyc.Service): self._callbacks: set[Callable[[str, Any], None]] = set() self._set_start_and_stop_for_async_methods() - self._register_list_change_callbacks(self, f"{self.__class__.__name__}") - self._register_DataService_instance_callbacks( - self, f"{self.__class__.__name__}" - ) - self._register_property_callbacks(self, f"{self.__class__.__name__}") + self._register_callbacks() self.__check_instance_classes() self._initialised = True @@ -119,6 +116,13 @@ class DataService(rpyc.Service): setattr(self, f"start_{name}", start_task) setattr(self, f"stop_{name}", stop_task) + def _register_callbacks(self) -> None: + self._register_list_change_callbacks(self, f"{self.__class__.__name__}") + self._register_DataService_instance_callbacks( + self, f"{self.__class__.__name__}" + ) + self._register_property_callbacks(self, f"{self.__class__.__name__}") + def _register_list_change_callbacks( self, obj: "DataService", parent_path: str ) -> None: @@ -407,6 +411,12 @@ class DataService(rpyc.Service): def _emit_notification(self, parent_path: str, name: str, value: Any) -> None: logger.debug(f"{parent_path}.{name} changed to {value}!") + for callback in self._notification_callbacks: + try: + callback(parent_path, name, value) + except Exception as e: + logger.error(e) + def serialize(self, prefix: str = "") -> dict[str, dict[str, Any]]: """ Serializes the instance into a dictionary, preserving the structure of the @@ -465,7 +475,6 @@ class DataService(rpyc.Service): "type": type(value).__name__, "value": value.serialize(prefix=key), "readonly": False, - "id": id(value), "doc": inspect.getdoc(value), } elif isinstance(value, list): @@ -515,3 +524,22 @@ class DataService(rpyc.Service): } return result + + def add_notification_callback( + self, callback: Callable[[str, str, Any], None] + ) -> None: + """ + Adds a new notification callback function to the list of callbacks. + + This function is intended to be used for registering a function that will be + called whenever a the value of an attribute changes. + + Args: + callback (Callable[[str, str, Any], None]): The callback function to + register. + It should accept three parameters: + - parent_path (str): The parent path of the parameter. + - name (str): The name of the changed parameter. + - value (Any): The value of the parameter. + """ + self._notification_callbacks.append(callback)