feat: removing superfluous property accesses

This commit is contained in:
Mose Müller 2023-09-13 17:56:23 +02:00
parent 6eafe07ac7
commit c12bb87b2b
2 changed files with 43 additions and 20 deletions

View File

@ -19,6 +19,7 @@ from pydase.utils.helpers import (
get_component_class_names, get_component_class_names,
get_nested_value_from_DataService_by_path_and_key, get_nested_value_from_DataService_by_path_and_key,
get_object_attr_from_path, get_object_attr_from_path,
is_property_attribute,
parse_list_attr_and_index, parse_list_attr_and_index,
update_value_if_changed, update_value_if_changed,
) )
@ -58,6 +59,8 @@ class DataService(rpyc.Service, AbstractDataService):
self._load_values_from_json() self._load_values_from_json()
def __setattr__(self, __name: str, __value: Any) -> None: def __setattr__(self, __name: str, __value: Any) -> None:
# converting attributes that are not properties
if not isinstance(getattr(type(self), __name, None), property):
current_value = getattr(self, __name, None) current_value = getattr(self, __name, None)
# parse ints into floats if current value is a float # parse ints into floats if current value is a float
if isinstance(current_value, float) and isinstance(__value, int): if isinstance(current_value, float) and isinstance(__value, int):
@ -84,6 +87,27 @@ class DataService(rpyc.Service, AbstractDataService):
if not attr_name.startswith("_DataService__"): if not attr_name.startswith("_DataService__"):
warn_if_instance_class_does_not_inherit_from_DataService(attr_value) warn_if_instance_class_does_not_inherit_from_DataService(attr_value)
def __set_attribute_based_on_type(
self,
target_obj: Any,
attr_name: str,
attr: Any,
value: Any,
index: Optional[int],
path_list: list[str],
) -> None:
if isinstance(attr, Enum):
update_value_if_changed(target_obj, attr_name, attr.__class__[value])
elif isinstance(attr, list) and index is not None:
update_value_if_changed(attr, index, value)
elif isinstance(attr, DataService) and isinstance(value, dict):
for key, v in value.items():
self.update_DataService_attribute([*path_list, attr_name], key, v)
elif callable(attr):
process_callable_attribute(attr, value["args"])
else:
update_value_if_changed(target_obj, attr_name, value)
def _rpyc_getattr(self, name: str) -> Any: def _rpyc_getattr(self, name: str) -> Any:
if name.startswith("_"): if name.startswith("_"):
# disallow special and private attributes # disallow special and private attributes
@ -338,24 +362,19 @@ class DataService(rpyc.Service, AbstractDataService):
) -> None: ) -> None:
# If attr_name corresponds to a list entry, extract the attr_name and the index # If attr_name corresponds to a list entry, extract the attr_name and the index
attr_name, index = parse_list_attr_and_index(attr_name) attr_name, index = parse_list_attr_and_index(attr_name)
# Traverse the object according to the path parts # Traverse the object according to the path parts
target_obj = get_object_attr_from_path(self, path_list) target_obj = get_object_attr_from_path(self, path_list)
attr = get_object_attr_from_path(target_obj, [attr_name]) # If the attribute is a property, change it using the setter without getting the
# property value (would otherwise be bad for expensive getter methods)
if is_property_attribute(target_obj, attr_name):
setattr(target_obj, attr_name, value)
return
attr = get_object_attr_from_path(target_obj, [attr_name])
if attr is None: if attr is None:
return return
# Set the attribute at the terminal point of the path self.__set_attribute_based_on_type(
if isinstance(attr, Enum): target_obj, attr_name, attr, value, index, path_list
update_value_if_changed(target_obj, attr_name, attr.__class__[value]) )
elif isinstance(attr, list) and index is not None:
update_value_if_changed(attr, index, value)
elif isinstance(attr, DataService) and isinstance(value, dict):
for key, v in value.items():
self.update_DataService_attribute([*path_list, attr_name], key, v)
elif callable(attr):
return process_callable_attribute(attr, value["args"])
else:
update_value_if_changed(target_obj, attr_name, value)

View File

@ -394,3 +394,7 @@ def get_component_class_names() -> list[str]:
import pydase.components import pydase.components
return pydase.components.__all__ return pydase.components.__all__
def is_property_attribute(target_obj: Any, attr_name: str) -> bool:
return isinstance(getattr(type(target_obj), attr_name, None), property)