Skip to content

Commit

Permalink
prevent adding the same child, parent, view twice in graph
Browse files Browse the repository at this point in the history
  • Loading branch information
nvaytet committed Oct 3, 2023
1 parent 598ff9d commit 880b2d4
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
26 changes: 19 additions & 7 deletions src/plopp/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@

import uuid
from itertools import chain
from typing import Any, Union
from typing import Any, List, Union

from .view import View


def _no_replace_append(container: List[Node], item: Node, kind: str):
"""
Append ``item`` to ``container`` if it is not already in it.
"""
if item in container:
tpe = 'View' if kind == 'view' else 'Node'
raise ValueError(f"{tpe} {item} is already a {kind} in {container}.")
container.append(item)


class Node:
"""
A node that can have parent and children nodes, to create a graph.
Expand Down Expand Up @@ -43,8 +53,6 @@ def __init__(self, func: Any, *parents, **kwparents):
self.kwparents = {
key: p if isinstance(p, Node) else Node(p) for key, p in kwparents.items()
}
for parent in chain(self.parents, self.kwparents.values()):
parent.children.append(self)
self._data = None

if func_is_callable:
Expand All @@ -61,6 +69,10 @@ def __init__(self, func: Any, *parents, **kwparents):
val_str = f'={repr(func)}' if isinstance(func, (int, float, str)) else ""
self.name = f'Input <{type(func).__name__}{val_str}>'

# Attempt to set children after setting name in case error message is needed
for parent in chain(self.parents, self.kwparents.values()):
_no_replace_append(parent.children, self, 'child')

def __call__(self):
return self.request_data()

Expand Down Expand Up @@ -125,22 +137,22 @@ def add_parents(self, *parents: Node):
Add one or more parents to the node.
"""
for parent in parents:
self.parents.append(parent)
parent.children.append(self)
_no_replace_append(self.parents, parent, 'parent')
_no_replace_append(parent.children, self, 'child')

def add_kwparents(self, **parents: Node):
"""
Add one or more keyword parents to the node.
"""
for key, parent in parents.items():
self.kwparents[key] = parent
parent.children.append(self)
_no_replace_append(parent.children, self, 'child')

def add_view(self, view: View):
"""
Add a view to the node.
"""
self.views.append(view)
_no_replace_append(self.views, view, 'view')
view.graph_nodes[self.id] = self

def notify_children(self, message: Any):
Expand Down
37 changes: 37 additions & 0 deletions tests/core/node_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,40 @@ def test_add_multiple_kwparents():
assert d in a.children
assert d in b.children
assert d in c.children


def test_adding_same_child_twice_raises():
a = Node(lambda: 5)
with pytest.raises(ValueError, match="Node .* is already a child in"):
Node(lambda x, y: x * y - 2, a, a)
with pytest.raises(ValueError, match="Node .* is already a child in"):
Node(lambda x, y: x * y - 2, x=a, y=a)


def test_adding_same_parent_twice_raises():
a = Node(lambda: 5)
b = Node(lambda x, y: x * y - 2)
b.add_parents(a)
with pytest.raises(ValueError, match="Node .* is already a parent in"):
b.add_parents(a)


def test_adding_same_parent_twice_at_once_raises():
a = Node(lambda: 5)
b = Node(lambda x, y: x * y - 2)
with pytest.raises(ValueError, match="Node .* is already a parent in"):
b.add_parents(a, a)


def test_adding_same_kwparent_twice_raises():
a = Node(lambda: 5)
b = Node(lambda x, y: x * y - 2)
with pytest.raises(ValueError, match="Node .* is already a child in"):
b.add_kwparents(x=a, y=a)


def test_adding_same_view_twice_raises():
a = Node(lambda: 15.0)
av = SimpleView(a)
with pytest.raises(ValueError, match="View .* is already a view in"):
a.add_view(av)

0 comments on commit 880b2d4

Please sign in to comment.