Use weak references in dict/list mappping to avoid memory leak

This commit is contained in:
Martin Stadler 2024-05-20 17:25:10 +02:00 committed by B108 Rpi Server
parent 46868743c7
commit 314e89ba38

View File

@ -4,6 +4,7 @@ from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, ClassVar, SupportsIndex
from pydase.utils.helpers import parse_serialized_key
import weakref
if TYPE_CHECKING:
from pydase.observer_pattern.observer.observer import Observer
@ -88,19 +89,17 @@ class ObservableObject(ABC):
if isinstance(value, list):
if id(value) in self._list_mapping:
# If the list `value` was already referenced somewhere else
new_value = self._list_mapping[id(value)]
new_value = self._list_mapping[id(value)]()
else:
# convert the builtin list into a ObservableList
new_value = _ObservableList(original_list=value)
self._list_mapping[id(value)] = new_value
elif isinstance(value, dict):
if id(value) in self._dict_mapping:
# If the dict `value` was already referenced somewhere else
new_value = self._dict_mapping[id(value)]
new_value = self._dict_mapping[id(value)]()
else:
# convert the builtin list into a ObservableList
# convert the builtin dict into a ObservableDict
new_value = _ObservableDict(original_dict=value)
self._dict_mapping[id(value)] = new_value
if isinstance(new_value, ObservableObject):
new_value.add_observer(self, attr_name_or_key)
return new_value
@ -138,6 +137,10 @@ class _ObservableList(ObservableObject, list[Any]):
list.__init__(self, self._original_list)
for i, item in enumerate(self._original_list):
super().__setitem__(i, self._initialise_new_objects(f"[{i}]", item))
self._list_mapping[id(self._original_list)] = weakref.ref(self)
def __del__(self):
self._list_mapping.pop(id(self._original_list))
def __setitem__(self, key: int, value: Any) -> None: # type: ignore[override]
if hasattr(self, "_observers"):
@ -236,6 +239,10 @@ class _ObservableDict(ObservableObject, dict[str, Any]):
dict.__init__(self)
for key, value in self._original_dict.items():
self.__setitem__(key, self._initialise_new_objects(f'["{key}"]', value))
self._dict_mapping[id(self._original_dict)] = weakref.ref(self)
def __del__(self):
self._dict_mapping.pop(id(self._original_dict))
def __setitem__(self, key: str, value: Any) -> None:
if not isinstance(key, str):