From fb78e96994eec2383738e8666cb0cb094bc53095 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:57:47 +0000 Subject: [PATCH 1/2] First Pass --- .../graph_traversal/default_traversal.py | 28 +++++++++++++++++++ .../objects/graph_traversal/traversal.py | 15 +++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/specklepy/objects/graph_traversal/default_traversal.py diff --git a/src/specklepy/objects/graph_traversal/default_traversal.py b/src/specklepy/objects/graph_traversal/default_traversal.py new file mode 100644 index 00000000..8c02dfb0 --- /dev/null +++ b/src/specklepy/objects/graph_traversal/default_traversal.py @@ -0,0 +1,28 @@ +from specklepy.objects.base import Base +from specklepy.objects.graph_traversal.traversal import GraphTraversal, TraversalRule + +DISPLAY_VALUE_PROPERTY_ALIASES = {"displayValue", "@displayValue"} +ELEMENTS_PROPERTY_ALIASES = {"elements", "@elements"} + + +def has_display_value(x: Base): + return any(hasattr(x, alias) for alias in DISPLAY_VALUE_PROPERTY_ALIASES) + + +def create_default_traversal_function() -> GraphTraversal: + """ + Traversal func for traversing the root object of a Speckle Model + """ + + convertible_rule = TraversalRule( + [lambda b: b.speckle_type != "Base", has_display_value], + lambda _: ELEMENTS_PROPERTY_ALIASES, + ) + + default_rule = TraversalRule( + [lambda _: True], + lambda o: o.get_member_names(), # NOTE: Unlike the C# implementation, this does not ignore Obsolete members + False, + ) + + return GraphTraversal([convertible_rule, default_rule]) diff --git a/src/specklepy/objects/graph_traversal/traversal.py b/src/specklepy/objects/graph_traversal/traversal.py index b32d0536..6ab16a7a 100644 --- a/src/specklepy/objects/graph_traversal/traversal.py +++ b/src/specklepy/objects/graph_traversal/traversal.py @@ -7,6 +7,11 @@ class ITraversalRule(Protocol): + + @property + def should_return(self) -> bool: + pass + def get_members_to_traverse(self, o: Base) -> Set[str]: """Get the members to traverse.""" pass @@ -50,10 +55,13 @@ def traverse(self, root: Base) -> Iterator[TraversalContext]: while len(stack) > 0: head = stack.pop() - yield head current = head.current active_rule = self._get_active_rule_or_default_rule(current) + + if active_rule.should_return: + yield head + members_to_traverse = active_rule.get_members_to_traverse(current) for child_prop in members_to_traverse: try: @@ -114,6 +122,11 @@ def _get_active_rule(self, o: Base) -> Optional[ITraversalRule]: class TraversalRule: _conditions: Collection[Callable[[Base], bool]] _members_to_traverse: Callable[[Base], Iterable[str]] + _should_return_to_output: bool = True + + @property + def should_return(self) -> bool: + return self._should_return_to_output def get_members_to_traverse(self, o: Base) -> Set[str]: return set(self._members_to_traverse(o)) From 17fb320f20da6ccb329c1f3f76144764d6cea680 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:05:47 +0000 Subject: [PATCH 2/2] Updated traversal --- .../objects/graph_traversal/default_traversal.py | 3 ++- src/specklepy/objects/graph_traversal/traversal.py | 1 - tests/unit/test_traverse_value.py | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/specklepy/objects/graph_traversal/default_traversal.py b/src/specklepy/objects/graph_traversal/default_traversal.py index 8c02dfb0..7729b0ed 100644 --- a/src/specklepy/objects/graph_traversal/default_traversal.py +++ b/src/specklepy/objects/graph_traversal/default_traversal.py @@ -21,7 +21,8 @@ def create_default_traversal_function() -> GraphTraversal: default_rule = TraversalRule( [lambda _: True], - lambda o: o.get_member_names(), # NOTE: Unlike the C# implementation, this does not ignore Obsolete members + # NOTE: Unlike the C# implementation, this does not ignore Obsolete members + lambda o: o.get_member_names(), False, ) diff --git a/src/specklepy/objects/graph_traversal/traversal.py b/src/specklepy/objects/graph_traversal/traversal.py index 6cee4159..1c980f03 100644 --- a/src/specklepy/objects/graph_traversal/traversal.py +++ b/src/specklepy/objects/graph_traversal/traversal.py @@ -7,7 +7,6 @@ class ITraversalRule(Protocol): - @property def should_return(self) -> bool: pass diff --git a/tests/unit/test_traverse_value.py b/tests/unit/test_traverse_value.py index 47e8c75b..e6932c11 100644 --- a/tests/unit/test_traverse_value.py +++ b/tests/unit/test_traverse_value.py @@ -1,5 +1,5 @@ -from typing import List from dataclasses import dataclass +from typing import List from specklepy.objects.base import Base from specklepy.serialization.base_object_serializer import BaseObjectSerializer @@ -12,16 +12,14 @@ class FakeBase(Base): def test_traverse_value(): - base = FakeBase(bar=1) - base.foo = [None] + base = FakeBase(bar=1, foo=["abcd"]) serializer = BaseObjectSerializer() object_id, object_dict = serializer.traverse_base(base) assert object_dict == { "id": object_id, "speckle_type": "Tests.Unit.TestTraverseValue.FakeBase", "applicationId": None, - "foo": [None], - "units": None, + "foo": ["abcd"], "bar": 1, "totalChildrenCount": 0, }