Merge pull request #30 from tiqi-group/29-protected-lists-crash-pydase

fix: removes notification for updating protected lists
This commit is contained in:
Mose Müller 2023-10-12 14:25:23 +02:00 committed by GitHub
commit 4abea8785c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 5 deletions

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pydase" name = "pydase"
version = "0.2.0" version = "0.2.1"
description = "A flexible and robust Python library for creating, managing, and interacting with data services, with built-in support for web and RPC servers, and customizable features for diverse use cases." description = "A flexible and robust Python library for creating, managing, and interacting with data services, with built-in support for web and RPC servers, and customizable features for diverse use cases."
authors = ["Mose Mueller <mosmuell@ethz.ch>"] authors = ["Mose Mueller <mosmuell@ethz.ch>"]
readme = "README.md" readme = "README.md"

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import inspect import inspect
from collections.abc import Callable from collections.abc import Callable
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any, cast
from loguru import logger from loguru import logger
@ -55,9 +55,10 @@ class CallbackManager:
self, obj: "AbstractDataService", parent_path: str self, obj: "AbstractDataService", parent_path: str
) -> None: ) -> None:
""" """
This method ensures that notifications are emitted whenever a list attribute of This method ensures that notifications are emitted whenever a public list
a DataService instance changes. These notifications pertain solely to the list attribute of a DataService instance changes. These notifications pertain solely
item changes, not to changes in attributes of objects within the list. to the list item changes, not to changes in attributes of objects within the
list.
The method works by converting all list attributes (both at the class and The method works by converting all list attributes (both at the class and
instance levels) into DataServiceList objects. Each DataServiceList is then instance levels) into DataServiceList objects. Each DataServiceList is then
@ -101,6 +102,8 @@ class CallbackManager:
value=value, value=value,
) )
if self.service == self.service.__root__ if self.service == self.service.__root__
# Skip private and protected lists
and not cast(str, attr_name).startswith("_")
else None else None
) )
@ -109,9 +112,12 @@ class CallbackManager:
attr_value.add_callback(callback) attr_value.add_callback(callback)
continue continue
if id(attr_value) in self._list_mapping: if id(attr_value) in self._list_mapping:
# If the list `attr_value` was already referenced somewhere else
notifying_list = self._list_mapping[id(attr_value)] notifying_list = self._list_mapping[id(attr_value)]
notifying_list.add_callback(callback) notifying_list.add_callback(callback)
else: else:
# convert the builtin list into a DataServiceList and add the
# callback
notifying_list = DataServiceList(attr_value, callback=[callback]) notifying_list = DataServiceList(attr_value, callback=[callback])
self._list_mapping[id(attr_value)] = notifying_list self._list_mapping[id(attr_value)] = notifying_list

View File

@ -99,3 +99,29 @@ def test_nested_reused_instance_list_attribute(capsys: CaptureFixture) -> None:
) )
actual_output = sorted(captured.out.strip().split("\n")) actual_output = sorted(captured.out.strip().split("\n"))
assert actual_output == expected_output assert actual_output == expected_output
def test_protected_list_attribute(capsys: CaptureFixture) -> None:
"""Changing protected lists should not emit notifications for the lists themselves, but
still for all properties depending on them.
"""
class ServiceClass(DataService):
_attr = [0, 1]
@property
def list_dependend_property(self) -> int:
return self._attr[0]
service_instance = ServiceClass()
service_instance._attr[0] = 1337
captured = capsys.readouterr()
expected_output = sorted(
[
"ServiceClass.list_dependend_property = 1337",
]
)
actual_output = sorted(captured.out.strip().split("\n")) # type: ignore
assert actual_output == expected_output