feat(serialization): simplify logic and align with core

only allow detaching of base objects
This commit is contained in:
izzy lyseggen
2020-11-30 16:42:24 +00:00
parent e2079ff6a9
commit 72dfd4807e
+27 -55
View File
@@ -1,9 +1,10 @@
import json
import hashlib
from uuid import uuid4
from typing import Any, Dict, List, Tuple
from speckle.objects.base import Base
from speckle.logging.exceptions import SerializationException
from speckle.logging.exceptions import SerializationException, SpeckleException
from speckle.transports.abstract_transport import AbstractTransport
PRIMITIVES = (int, float, str, bool)
@@ -14,15 +15,18 @@ def hash_obj(obj: Any) -> str:
class BaseObjectSerializer:
read_transport: AbstractTransport
write_transports: List[AbstractTransport]
detach_lineage: List[bool] = [] # tracks depth and whether or not to detach
family_tree: List[Dict[str, int]] = [{}]
leaf: int = 0
traversed_objects: Dict[str, Dict] = {}
lineage: List[str] = [] # keeps track of hash chain through the object tree
family_tree: Dict[str, Dict[str, int]] = {}
closure_table: Dict[str, Dict[str, int]] = {}
def __init__(self, write_transports: List[AbstractTransport] = []) -> None:
def __init__(
self, write_transports: List[AbstractTransport] = [], read_transport=None
) -> None:
self.write_transports = write_transports
self.read_transport = read_transport
def write_json(self, base: Base):
self.detach_lineage = [True]
@@ -40,88 +44,56 @@ class BaseObjectSerializer:
"""
if not self.detach_lineage:
self.detach_lineage = [True]
self.lineage.append(uuid4().hex)
object_builder = {"id": ""}
children = []
obj, props = base, base.get_member_names()
while props:
prop = props.pop(0)
value = obj[prop]
detach = False
# skip nulls or props marked to be ignored with "__"
if not value or prop.startswith("__"):
continue
if prop.startswith("@"):
detach = True
detach = True if prop.startswith("@") else False
# 1. handle primitives (ints, floats, strings, and bools)
if isinstance(value, PRIMITIVES):
object_builder[prop] = value
continue
# 2. handle lists and dicts
elif isinstance(value, (list, dict)):
self.leaf += 1
self.family_tree.append({})
# 2. handle Base objects
elif isinstance(value, Base):
child_obj = self.traverse_value(value, detach=detach)
if detach:
child_obj = self.traverse_value(value, detach=True)
ref_hash = hash_obj(child_obj)
else:
child_obj = self.traverse_value(value)
self.leaf -= 1
for hash, depth in self.family_tree.pop().items():
if (
hash in self.family_tree[self.leaf]
and self.family_tree[self.leaf][hash] < depth
):
pass
else:
self.family_tree[self.leaf][hash] = depth
# 3. handle all other cases (nested Base objects, other objects, etc)
elif detach:
# self.detach_lineage.append(True)
child_obj = self.traverse_value(value, detach=True)
if isinstance(value, Base):
ref_hash = child_obj["id"]
else:
ref_hash = hash_obj(child_obj)
object_builder[prop] = self.detach_helper(ref_hash=ref_hash)
# 3. handle all other cases
else:
# self.detach_lineage.append(False)
child_obj = self.traverse_value(value)
# save child object (or the referenced to the detached child) to the object builder
if detach:
children.append(ref_hash)
object_builder[prop] = self.detach_helper(
ref_hash=ref_hash, obj=child_obj
)
else:
object_builder[prop] = child_obj
hash = hash_obj(object_builder)
object_builder["id"] = hash
lineage = self.detach_lineage.pop() if self.detach_lineage else False
detached = self.detach_lineage.pop()
# add closures to object and to closure table
if children:
lineage_length = len(self.detach_lineage)
# add closures to the object
if self.lineage[-1] in self.family_tree:
object_builder["__closure"] = self.closure_table[hash] = {
hash: minDepth - lineage_length if lineage_length > 0 else minDepth
for hash, minDepth in self.family_tree[self.leaf].items()
ref: depth - len(self.detach_lineage)
for ref, depth in self.family_tree[self.lineage[-1]].items()
}
# write detached or root objects to transports
if lineage:
self.traversed_objects[hash] = object_builder
self.write_to_transports(hash, object_builder)
if detached:
for t in self.write_transports:
t.save_object(id=hash, serialized_object=json.dumps(object_builder))
del self.lineage[-1]
return hash, object_builder