mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-06 05:30:39 +02:00
Merge pull request #197 from tiqi-group/fix/dict_key_normalization
Fix: dict key normalization
This commit is contained in:
commit
d6bad37233
@ -10,7 +10,6 @@ from pydase.observer_pattern.observer.property_observer import (
|
|||||||
)
|
)
|
||||||
from pydase.utils.helpers import (
|
from pydase.utils.helpers import (
|
||||||
get_object_attr_from_path,
|
get_object_attr_from_path,
|
||||||
normalize_full_access_path_string,
|
|
||||||
)
|
)
|
||||||
from pydase.utils.serialization.serializer import (
|
from pydase.utils.serialization.serializer import (
|
||||||
SerializationPathError,
|
SerializationPathError,
|
||||||
@ -102,8 +101,7 @@ class DataServiceObserver(PropertyObserver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _notify_dependent_property_changes(self, changed_attr_path: str) -> None:
|
def _notify_dependent_property_changes(self, changed_attr_path: str) -> None:
|
||||||
normalized_attr_path = normalize_full_access_path_string(changed_attr_path)
|
changed_props = self.property_deps_dict.get(changed_attr_path, [])
|
||||||
changed_props = self.property_deps_dict.get(normalized_attr_path, [])
|
|
||||||
for prop in changed_props:
|
for prop in changed_props:
|
||||||
# only notify about changing attribute if it is not currently being
|
# only notify about changing attribute if it is not currently being
|
||||||
# "changed" e.g. when calling the getter of a property within another
|
# "changed" e.g. when calling the getter of a property within another
|
||||||
|
@ -100,7 +100,7 @@ class PropertyObserver(Observer):
|
|||||||
elif isinstance(collection, dict):
|
elif isinstance(collection, dict):
|
||||||
for key, val in collection.items():
|
for key, val in collection.items():
|
||||||
if isinstance(val, Observable):
|
if isinstance(val, Observable):
|
||||||
new_prefix = f"{parent_path}['{key}']"
|
new_prefix = f'{parent_path}["{key}"]'
|
||||||
deps.update(
|
deps.update(
|
||||||
self._get_properties_and_their_dependencies(val, new_prefix)
|
self._get_properties_and_their_dependencies(val, new_prefix)
|
||||||
)
|
)
|
||||||
|
@ -223,25 +223,3 @@ def current_event_loop_exists() -> bool:
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
return asyncio.get_event_loop_policy()._local._loop is not None # type: ignore
|
return asyncio.get_event_loop_policy()._local._loop is not None # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def normalize_full_access_path_string(s: str) -> str:
|
|
||||||
"""Normalizes a string representing a full access path by converting double quotes
|
|
||||||
to single quotes.
|
|
||||||
|
|
||||||
This function is useful for ensuring consistency in strings that represent access
|
|
||||||
paths containing dictionary keys, by replacing all double quotes (`"`) with single
|
|
||||||
quotes (`'`).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
s (str): The input string to be normalized.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A new string with all double quotes replaced by single quotes.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
>>> normalize_full_access_path_string('dictionary["first"].my_task')
|
|
||||||
"dictionary['first'].my_task"
|
|
||||||
"""
|
|
||||||
|
|
||||||
return s.replace('"', "'")
|
|
||||||
|
@ -167,8 +167,8 @@ def test_normalized_attr_path_in_dependent_property_changes(
|
|||||||
state_manager = StateManager(service=service_instance)
|
state_manager = StateManager(service=service_instance)
|
||||||
observer = DataServiceObserver(state_manager=state_manager)
|
observer = DataServiceObserver(state_manager=state_manager)
|
||||||
|
|
||||||
assert observer.property_deps_dict["service_dict['one']._prop"] == [
|
assert observer.property_deps_dict['service_dict["one"]._prop'] == [
|
||||||
"service_dict['one'].prop"
|
'service_dict["one"].prop'
|
||||||
]
|
]
|
||||||
|
|
||||||
# We can use dict key path encoded with double quotes
|
# We can use dict key path encoded with double quotes
|
||||||
@ -184,3 +184,41 @@ def test_normalized_attr_path_in_dependent_property_changes(
|
|||||||
)
|
)
|
||||||
assert service_instance.service_dict["one"].prop == 12.0
|
assert service_instance.service_dict["one"].prop == 12.0
|
||||||
assert "'service_dict[\"one\"].prop' changed to '12.0'" in caplog.text
|
assert "'service_dict[\"one\"].prop' changed to '12.0'" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_dict_property_changes(
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
def get_voltage() -> float:
|
||||||
|
"""Mocking a remote device."""
|
||||||
|
return 2.0
|
||||||
|
|
||||||
|
def set_voltage(value: float) -> None:
|
||||||
|
"""Mocking a remote device."""
|
||||||
|
|
||||||
|
class OtherService(pydase.DataService):
|
||||||
|
_voltage = 1.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def voltage(self) -> float:
|
||||||
|
# Property dependency _voltage changes within the property itself.
|
||||||
|
# This should be handled gracefully, i.e. not introduce recursion
|
||||||
|
self._voltage = get_voltage()
|
||||||
|
return self._voltage
|
||||||
|
|
||||||
|
@voltage.setter
|
||||||
|
def voltage(self, value: float) -> None:
|
||||||
|
self._voltage = value
|
||||||
|
set_voltage(self._voltage)
|
||||||
|
|
||||||
|
class MyService(pydase.DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.my_dict = {"key": OtherService()}
|
||||||
|
|
||||||
|
service = MyService()
|
||||||
|
pydase.Server(service)
|
||||||
|
|
||||||
|
# Changing the _voltage attribute should re-evaluate the voltage property, but avoid
|
||||||
|
# recursion
|
||||||
|
service.my_dict["key"].voltage = 1.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user