mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-22 09:10:01 +02:00
refactoring Serializer class
This commit is contained in:
parent
2b57df5aac
commit
6804cdf3b1
@ -1,5 +1,6 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Callable
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
@ -19,130 +20,197 @@ class Serializer:
|
|||||||
"""
|
"""
|
||||||
attr_doc = inspect.getdoc(attr)
|
attr_doc = inspect.getdoc(attr)
|
||||||
attr_class_doc = inspect.getdoc(type(attr))
|
attr_class_doc = inspect.getdoc(type(attr))
|
||||||
if attr_class_doc != attr_doc:
|
return attr_doc if attr_class_doc != attr_doc else None
|
||||||
return attr_doc
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize_object(obj: Any):
|
def serialize_object(obj: Any) -> dict[str, Any]:
|
||||||
obj_type = type(obj).__name__
|
result: dict[str, Any] = {}
|
||||||
value = obj
|
|
||||||
readonly = False # You need to determine how to set this value
|
|
||||||
doc = Serializer.get_attribute_doc(obj)
|
|
||||||
kwargs: dict[str, Any] = {}
|
|
||||||
|
|
||||||
if isinstance(obj, AbstractDataService):
|
if isinstance(obj, AbstractDataService):
|
||||||
# Get the dictionary of the base class
|
result = Serializer._serialize_DataService(obj)
|
||||||
base_set = set(type(obj).__base__.__dict__)
|
|
||||||
# Get the dictionary of the derived class
|
|
||||||
derived_set = set(type(obj).__dict__)
|
|
||||||
# Get the difference between the two dictionaries
|
|
||||||
derived_only_set = derived_set - base_set
|
|
||||||
|
|
||||||
instance_dict = set(obj.__dict__)
|
elif isinstance(obj, list):
|
||||||
# Merge the class and instance dictionaries
|
result = Serializer._serialize_list(obj)
|
||||||
merged_set = derived_only_set | instance_dict
|
|
||||||
value = {}
|
|
||||||
|
|
||||||
if type(value).__name__ not in get_component_class_names():
|
elif isinstance(obj, dict):
|
||||||
obj_type = "DataService"
|
result = Serializer._serialize_dict(obj)
|
||||||
|
|
||||||
# Iterate over attributes, properties, class attributes, and methods
|
|
||||||
for key in sorted(merged_set):
|
|
||||||
if key.startswith("_"):
|
|
||||||
continue # Skip attributes that start with underscore
|
|
||||||
|
|
||||||
# Skip keys that start with "start_" or "stop_" and end with an async
|
|
||||||
# method name
|
|
||||||
if (key.startswith("start_") or key.startswith("stop_")) and key.split(
|
|
||||||
"_", 1
|
|
||||||
)[1] in {
|
|
||||||
name
|
|
||||||
for name, _ in inspect.getmembers(
|
|
||||||
obj, predicate=inspect.iscoroutinefunction
|
|
||||||
)
|
|
||||||
}:
|
|
||||||
continue
|
|
||||||
|
|
||||||
val = getattr(obj, key)
|
|
||||||
|
|
||||||
value[key] = Serializer.serialize_object(val)
|
|
||||||
|
|
||||||
# If there's a running task for this method
|
|
||||||
if key in obj._task_manager.tasks:
|
|
||||||
task_info = obj._task_manager.tasks[key]
|
|
||||||
value[key]["value"] = task_info["kwargs"]
|
|
||||||
|
|
||||||
# If the DataService attribute is a property
|
|
||||||
if isinstance(getattr(obj.__class__, key, None), property):
|
|
||||||
prop: property = getattr(obj.__class__, key)
|
|
||||||
value[key]["readonly"] = prop.fset is None
|
|
||||||
value[key]["doc"] = Serializer.get_attribute_doc(
|
|
||||||
prop
|
|
||||||
) # overwrite the doc
|
|
||||||
|
|
||||||
elif isinstance(value, list):
|
|
||||||
obj_type = "list"
|
|
||||||
value = [Serializer.serialize_object(o) for o in value]
|
|
||||||
|
|
||||||
elif isinstance(value, dict):
|
|
||||||
obj_type = "dict"
|
|
||||||
value = {key: Serializer.serialize_object(val) for key, val in obj.items()}
|
|
||||||
|
|
||||||
# Special handling for u.Quantity
|
# Special handling for u.Quantity
|
||||||
elif isinstance(obj, u.Quantity):
|
elif isinstance(obj, u.Quantity):
|
||||||
value = {"magnitude": obj.m, "unit": str(obj.u)}
|
result = Serializer._serialize_Quantity(obj)
|
||||||
|
|
||||||
# Handling for Enums
|
# Handling for Enums
|
||||||
elif isinstance(obj, Enum):
|
elif isinstance(obj, Enum):
|
||||||
value = obj.name
|
result = Serializer._serialize_enum(obj)
|
||||||
if type(obj).__base__.__name__ == "ColouredEnum":
|
|
||||||
obj_type = "ColouredEnum"
|
|
||||||
else:
|
|
||||||
obj_type = "Enum"
|
|
||||||
kwargs = {
|
|
||||||
"enum": {
|
|
||||||
name: member.value
|
|
||||||
for name, member in obj.__class__.__members__.items()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# Methods and coroutines
|
# Methods and coroutines
|
||||||
elif inspect.isfunction(obj) or inspect.ismethod(obj):
|
elif inspect.isfunction(obj) or inspect.ismethod(obj):
|
||||||
sig = inspect.signature(value)
|
result = Serializer._serialize_method(obj)
|
||||||
|
|
||||||
# Store parameters and their anotations in a dictionary
|
else:
|
||||||
parameters: dict[str, Optional[str]] = {}
|
obj_type = type(obj).__name__
|
||||||
for k, v in sig.parameters.items():
|
value = obj
|
||||||
annotation = v.annotation
|
readonly = False
|
||||||
if annotation is not inspect._empty:
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
if isinstance(annotation, type):
|
result = {
|
||||||
# Handle regular types
|
"type": obj_type,
|
||||||
parameters[k] = annotation.__name__
|
"value": value,
|
||||||
else:
|
"readonly": readonly,
|
||||||
# Union, string annotation, Literal types, ...
|
"doc": doc,
|
||||||
parameters[k] = str(annotation)
|
|
||||||
else:
|
|
||||||
parameters[k] = None
|
|
||||||
value = None
|
|
||||||
obj_type = "method"
|
|
||||||
readonly = True
|
|
||||||
kwargs = {
|
|
||||||
"async": inspect.iscoroutinefunction(obj),
|
|
||||||
"parameters": parameters,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Construct the result dictionary
|
return result
|
||||||
result = {
|
|
||||||
|
@staticmethod
|
||||||
|
def _serialize_enum(obj: Enum) -> dict[str, Any]:
|
||||||
|
value = obj.name
|
||||||
|
readonly = False
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
if type(obj).__base__.__name__ == "ColouredEnum":
|
||||||
|
obj_type = "ColouredEnum"
|
||||||
|
else:
|
||||||
|
obj_type = "Enum"
|
||||||
|
|
||||||
|
return {
|
||||||
"type": obj_type,
|
"type": obj_type,
|
||||||
"value": value,
|
"value": value,
|
||||||
"readonly": readonly,
|
"readonly": readonly,
|
||||||
"doc": doc,
|
"doc": doc,
|
||||||
**kwargs,
|
"enum": {
|
||||||
|
name: member.value for name, member in obj.__class__.__members__.items()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
@staticmethod
|
||||||
|
def _serialize_Quantity(obj: u.Quantity) -> dict[str, Any]:
|
||||||
|
obj_type = "Quantity"
|
||||||
|
readonly = False
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
value = {"magnitude": obj.m, "unit": str(obj.u)}
|
||||||
|
return {
|
||||||
|
"type": obj_type,
|
||||||
|
"value": value,
|
||||||
|
"readonly": readonly,
|
||||||
|
"doc": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _serialize_dict(obj: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
obj_type = "dict"
|
||||||
|
readonly = False
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
value = {key: Serializer.serialize_object(val) for key, val in obj.items()}
|
||||||
|
return {
|
||||||
|
"type": obj_type,
|
||||||
|
"value": value,
|
||||||
|
"readonly": readonly,
|
||||||
|
"doc": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _serialize_list(obj: list[Any]) -> dict[str, Any]:
|
||||||
|
obj_type = "list"
|
||||||
|
readonly = False
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
value = [Serializer.serialize_object(o) for o in obj]
|
||||||
|
return {
|
||||||
|
"type": obj_type,
|
||||||
|
"value": value,
|
||||||
|
"readonly": readonly,
|
||||||
|
"doc": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _serialize_method(obj: Callable[..., Any]) -> dict[str, Any]:
|
||||||
|
obj_type = "method"
|
||||||
|
value = None
|
||||||
|
readonly = True
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
|
||||||
|
# Store parameters and their anotations in a dictionary
|
||||||
|
sig = inspect.signature(obj)
|
||||||
|
parameters: dict[str, Optional[str]] = {}
|
||||||
|
|
||||||
|
for k, v in sig.parameters.items():
|
||||||
|
annotation = v.annotation
|
||||||
|
if annotation is not inspect._empty:
|
||||||
|
if isinstance(annotation, type):
|
||||||
|
# Handle regular types
|
||||||
|
parameters[k] = annotation.__name__
|
||||||
|
else:
|
||||||
|
# Union, string annotation, Literal types, ...
|
||||||
|
parameters[k] = str(annotation)
|
||||||
|
else:
|
||||||
|
parameters[k] = None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": obj_type,
|
||||||
|
"value": value,
|
||||||
|
"readonly": readonly,
|
||||||
|
"doc": doc,
|
||||||
|
"async": inspect.iscoroutinefunction(obj),
|
||||||
|
"parameters": parameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _serialize_DataService(obj: AbstractDataService) -> dict[str, Any]:
|
||||||
|
readonly = False
|
||||||
|
doc = Serializer.get_attribute_doc(obj)
|
||||||
|
obj_type = type(obj).__name__
|
||||||
|
if type(obj).__name__ not in get_component_class_names():
|
||||||
|
obj_type = "DataService"
|
||||||
|
|
||||||
|
# Get the dictionary of the base class
|
||||||
|
base_set = set(type(obj).__base__.__dict__)
|
||||||
|
# Get the dictionary of the derived class
|
||||||
|
derived_set = set(type(obj).__dict__)
|
||||||
|
# Get the difference between the two dictionaries
|
||||||
|
derived_only_set = derived_set - base_set
|
||||||
|
|
||||||
|
instance_dict = set(obj.__dict__)
|
||||||
|
# Merge the class and instance dictionaries
|
||||||
|
merged_set = derived_only_set | instance_dict
|
||||||
|
value = {}
|
||||||
|
|
||||||
|
# Iterate over attributes, properties, class attributes, and methods
|
||||||
|
for key in sorted(merged_set):
|
||||||
|
if key.startswith("_"):
|
||||||
|
continue # Skip attributes that start with underscore
|
||||||
|
|
||||||
|
# Skip keys that start with "start_" or "stop_" and end with an async
|
||||||
|
# method name
|
||||||
|
if (key.startswith("start_") or key.startswith("stop_")) and key.split(
|
||||||
|
"_", 1
|
||||||
|
)[1] in {
|
||||||
|
name
|
||||||
|
for name, _ in inspect.getmembers(
|
||||||
|
obj, predicate=inspect.iscoroutinefunction
|
||||||
|
)
|
||||||
|
}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
val = getattr(obj, key)
|
||||||
|
|
||||||
|
value[key] = Serializer.serialize_object(val)
|
||||||
|
|
||||||
|
# If there's a running task for this method
|
||||||
|
if key in obj._task_manager.tasks:
|
||||||
|
task_info = obj._task_manager.tasks[key]
|
||||||
|
value[key]["value"] = task_info["kwargs"]
|
||||||
|
|
||||||
|
# If the DataService attribute is a property
|
||||||
|
if isinstance(getattr(obj.__class__, key, None), property):
|
||||||
|
prop: property = getattr(obj.__class__, key)
|
||||||
|
value[key]["readonly"] = prop.fset is None
|
||||||
|
value[key]["doc"] = Serializer.get_attribute_doc(
|
||||||
|
prop
|
||||||
|
) # overwrite the doc
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": obj_type,
|
||||||
|
"value": value,
|
||||||
|
"readonly": readonly,
|
||||||
|
"doc": doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def dump(obj: Any) -> dict[str, Any]:
|
def dump(obj: Any) -> dict[str, Any]:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user