moving set_nested_value_in_dict to Serializer, renaming module

This commit is contained in:
Mose Müller 2023-11-06 18:26:38 +01:00
parent 4ef4bab36e
commit 3440a632ad
5 changed files with 83 additions and 89 deletions

View File

@ -20,7 +20,7 @@ from pydase.utils.helpers import (
parse_list_attr_and_index,
update_value_if_changed,
)
from pydase.utils.serialization import Serializer
from pydase.utils.serializer import Serializer
from pydase.utils.warnings import (
warn_if_instance_class_does_not_inherit_from_DataService,
)

View File

@ -1,7 +1,7 @@
import logging
from typing import TYPE_CHECKING, Any
from pydase.utils.helpers import set_nested_value_in_dict
from pydase.utils.serializer import Serializer
if TYPE_CHECKING:
from pydase import DataService
@ -32,5 +32,5 @@ class DataServiceCache:
# Construct the full path
full_path = f"{parent_path}.{name}" if parent_path else name
set_nested_value_in_dict(self._cache, full_path, value)
Serializer.update_serialization_dict(self._cache, full_path, value)
logger.debug(f"Cache updated at path: {full_path}, with value: {value}")

View File

@ -272,89 +272,6 @@ def get_nested_value_from_DataService_by_path_and_key(
return current_data.get(key, None)
def set_nested_value_in_dict(data_dict: dict[str, Any], path: str, value: Any) -> None:
"""
Set the value associated with a specific key in a dictionary given a path.
This function traverses the dictionary according to the path provided and
sets the value at that path. The path is a string with dots connecting
the levels and brackets indicating list indices.
Args:
cache (dict): The cache dictionary to set the value in.
path (str): The path to where the value should be set in the dictionary.
value (Any): The value to be set at the specified path in the dictionary.
Examples:
Let's consider the following dictionary:
cache = {
"attr1": {"type": "int", "value": 10},
"attr2": {
"type": "MyClass",
"value": {"attr3": {"type": "float", "value": 20.5}}
}
}
The function can be used to set the value of 'attr1' as follows:
set_nested_value_in_cache(cache, "attr1", 15)
It can also be used to set the value of 'attr3', which is nested within 'attr2',
as follows:
set_nested_value_in_cache(cache, "attr2.attr3", 25.0)
"""
parts = path.split(".")
current_dict: dict[str, Any] = data_dict
index: Optional[int] = None
for attr_name in parts:
# Check if the key contains an index part like '[<index>]'
if "[" in attr_name and attr_name.endswith("]"):
attr_name, index_part = attr_name.split("[", 1)
index_part = index_part.rstrip("]") # remove the closing bracket
# Convert the index part to an integer
if index_part.isdigit():
index = int(index_part)
else:
logger.error(f"Invalid index format in key: {attr_name}")
current_dict = cast(dict[str, Any], current_dict.get(attr_name, None))
if not isinstance(current_dict, dict):
# key does not exist in dictionary, e.g. when class does not have this
# attribute
return
if index is not None:
if 0 <= index < len(current_dict["value"]):
try:
current_dict = cast(dict[str, Any], current_dict["value"][index])
except Exception as e:
logger.error(f"Could not change {path}. Exception: {e}")
return
else:
# TODO: appending to a list will probably be done here
logger.error(f"Could not change {path}...")
return
# When the attribute is a class instance, the attributes are nested in the
# "value" key
if (
current_dict["type"] not in STANDARD_TYPES
and current_dict["type"] != "method"
):
current_dict = cast(dict[str, Any], current_dict.get("value", None)) # type: ignore
index = None
# setting the new value
try:
current_dict["value"] = value
except Exception as e:
logger.error(e)
def convert_arguments_to_hinted_types(
args: dict[str, Any], type_hints: dict[str, Any]

View File

@ -2,11 +2,15 @@ import inspect
import logging
from collections.abc import Callable
from enum import Enum
from typing import Any, Optional
from typing import Any, Optional, cast
import pydase.units as u
from pydase.data_service.abstract_data_service import AbstractDataService
from pydase.utils.helpers import get_component_class_names
from pydase.utils.helpers import (
STANDARD_TYPES,
get_component_class_names,
parse_list_attr_and_index,
)
logger = logging.getLogger(__name__)
@ -212,6 +216,79 @@ class Serializer:
"doc": doc,
}
@staticmethod
def update_serialization_dict(
serialization_dict: dict[str, Any], path: str, value: Any
) -> None:
"""
Set the value associated with a specific key in a dictionary given a path.
This function traverses the dictionary according to the path provided and
sets the value at that path. The path is a string with dots connecting
the levels and brackets indicating list indices.
Args:
data_dict (dict): The cache dictionary to set the value in.
path (str): The path to where the value should be set in the dictionary.
value (Any): The value to be set at the specified path in the dictionary.
Examples:
Let's consider the following dictionary:
cache = {
"attr1": {"type": "int", "value": 10},
"attr2": {
"type": "MyClass",
"value": {"attr3": {"type": "float", "value": 20.5}}
}
}
The function can be used to set the value of 'attr1' as follows:
set_nested_value_in_cache(cache, "attr1", 15)
It can also be used to set the value of 'attr3', which is nested within
'attr2', as follows:
set_nested_value_in_cache(cache, "attr2.attr3", 25.0)
"""
parts, attr_name = path.split(".")[:-1], path.split(".")[-1]
current_dict: dict[str, Any] = serialization_dict
index: Optional[int] = None
for path_part in parts:
# Check if the key contains an index part like 'attr_name[<index>]'
path_part, index = parse_list_attr_and_index(path_part)
current_dict = cast(dict[str, Any], current_dict.get(path_part, None))
if not isinstance(current_dict, dict):
# key does not exist in dictionary, e.g. when class does not have this
# attribute
return
if index is not None:
try:
current_dict = cast(dict[str, Any], current_dict["value"][index])
except Exception as e:
# TODO: appending to a list will probably be done here
logger.error(f"Could not change {path}... {e}")
return
# When the attribute is a class instance, the attributes are nested in the
# "value" key
if (
current_dict["type"] not in STANDARD_TYPES
and current_dict["type"] != "method"
):
current_dict = cast(dict[str, Any], current_dict.get("value", None)) # type: ignore
index = None
# setting the new value
serialized_value = dump(value)
current_dict[attr_name]["value"] = serialized_value["value"]
current_dict[attr_name]["type"] = serialized_value["type"]
def dump(obj: Any) -> dict[str, Any]:
return Serializer.serialize_object(obj)

View File

@ -6,7 +6,7 @@ import pytest
import pydase
import pydase.units as u
from pydase.components.coloured_enum import ColouredEnum
from pydase.utils.serialization import dump
from pydase.utils.serializer import dump
@pytest.mark.parametrize(