Skip to content

Commit c9de21a

Browse files
committed
ENH(net,+1TC): check Node-type CLASHES
1 parent 5635ae5 commit c9de21a

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

graphtik/planning.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
#: is augmented with children's details.
5252
log = logging.getLogger(__name__)
5353

54+
NODE_TYPE = {0: "dependency", 1: "operation"}
55+
5456

5557
def yield_datanodes(nodes) -> List[str]:
5658
"""May scan dag nodes."""
@@ -497,16 +499,34 @@ def append_subdoc_chain(doc_parts):
497499
doc_chain = [p for p in doc_chain if p]
498500
graph.add_edges_from(unseen_subdoc_edges(pairwise(doc_chain)))
499501

502+
node_types = graph.nodes(data="typ") # a view to check for collisions
503+
504+
def check_node_collision(
505+
node, node_type: int, dep_op=None, dep_kind: str = None
506+
):
507+
"""The dep_op/dep_kind are given only for data-nodes."""
508+
if node in node_types and node_types[node] != node_type:
509+
assert not (bool(dep_op) ^ bool(dep_kind)), locals()
510+
graph_type = NODE_TYPE[node_types[node]]
511+
given_node = (
512+
f"{dep_kind}({node!r})" if dep_op else f"operation({node.name})"
513+
)
514+
dep_op = f"\n +--owning op: {dep_op}" if dep_op else ""
515+
raise ValueError(
516+
f"Name of {given_node} clashed with a same-named {graph_type} in graph!{dep_op}"
517+
)
518+
500519
## Needs
501520
#
502521
needs = []
503522
needs_edges = []
504523
for n in operation.needs:
505524
json_path = get_jsonp(n)
525+
check_node_collision(n, 0, operation, "needs")
506526
if json_path:
507527
append_subdoc_chain(json_path)
508528

509-
nkw, ekw = {}, {} # node, edge props
529+
nkw, ekw = {"typ": 0}, {} # node, edge props
510530
if is_optional(n):
511531
ekw["optional"] = True
512532
if is_sfx(n):
@@ -520,7 +540,8 @@ def append_subdoc_chain(doc_parts):
520540
needs_edges.append((n, operation, ekw))
521541
graph.add_nodes_from(needs)
522542
node_props = getattr(operation, "node_props", None) or {}
523-
graph.add_node(operation, **node_props)
543+
check_node_collision(operation, 1)
544+
graph.add_node(operation, typ=1, **node_props)
524545
graph.add_edges_from(needs_edges)
525546

526547
## Prepare inversed-aliases index, used
@@ -533,20 +554,21 @@ def append_subdoc_chain(doc_parts):
533554
#
534555
for n in operation.provides:
535556
json_path = get_jsonp(n)
557+
check_node_collision(n, 0, operation, "provides")
536558
if json_path:
537559
append_subdoc_chain(json_path)
538560

539-
kw = {}
561+
nkw, ekw = {"typ": 0}, {}
540562
if is_sfx(n):
541-
kw["sideffect"] = True
542-
graph.add_node(n, sideffect=True)
563+
ekw["sideffect"] = nkw["sideffect"] = True
543564
if is_implicit(n):
544-
kw["implicit"] = True
565+
ekw["implicit"] = True
545566

546567
if n in alias_destinations:
547-
kw["alias_of"] = alias_destinations[n]
568+
ekw["alias_of"] = alias_destinations[n]
548569

549-
graph.add_edge(operation, n, **kw)
570+
graph.add_node(n, **nkw)
571+
graph.add_edge(operation, n, **ekw)
550572

551573
def _apply_graph_predicate(self, graph, predicate):
552574
to_del = []

test/test_op.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,10 @@ def test_pipeline_node_props():
302302
op2 = operation(lambda: None, name="b", node_props={"a": 3, "c": 4})
303303
pipeline = compose("n", op1, op2, node_props={"bb": 22, "c": 44})
304304

305-
exp = {"a": {"a": 11, "b": 0, "bb": 22, "c": 44}, "b": {"a": 3, "bb": 22, "c": 44}}
305+
exp = {
306+
"a": {"typ": 1, "a": 11, "b": 0, "bb": 22, "c": 44},
307+
"b": {"typ": 1, "a": 3, "bb": 22, "c": 44},
308+
}
306309
node_props = _collect_op_props(pipeline)
307310
assert node_props == exp
308311

@@ -322,17 +325,20 @@ def test_pipeline_merge_node_props():
322325
pipeline = compose(
323326
"n", pipeline1, pipeline2, node_props={"bb": 22, "c": 44}, nest=False
324327
)
325-
exp = {"a": {"a": 1, "bb": 22, "c": 44}, "b": {"a": 3, "bb": 22, "c": 44}}
328+
exp = {
329+
"a": {"typ": 1, "a": 1, "bb": 22, "c": 44},
330+
"b": {"typ": 1, "a": 3, "bb": 22, "c": 44},
331+
}
326332
node_props = _collect_op_props(pipeline)
327333
assert node_props == exp
328334

329335
pipeline = compose(
330336
"n", pipeline1, pipeline2, node_props={"bb": 22, "c": 44}, nest=True
331337
)
332338
exp = {
333-
"n1.a": {"a": 1, "bb": 22, "c": 44},
334-
"n2.a": {"a": 11, "b": 0, "bb": 22, "c": 44},
335-
"n2.b": {"a": 3, "bb": 22, "c": 44},
339+
"n1.a": {"typ": 1, "a": 1, "bb": 22, "c": 44},
340+
"n2.a": {"typ": 1, "a": 11, "b": 0, "bb": 22, "c": 44},
341+
"n2.b": {"typ": 1, "a": 3, "bb": 22, "c": 44},
336342
}
337343
node_props = _collect_op_props(pipeline)
338344
assert node_props == exp

test/test_planning.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import pytest
77
from networkx.readwrite.edgelist import parse_edgelist
88

9+
from graphtik import operation
910
from graphtik.planning import (
1011
yield_also_chaindocs,
1112
yield_also_subdocs,
1213
yield_also_superdocs,
1314
yield_chaindocs,
1415
yield_subdocs,
1516
yield_superdocs,
17+
Network,
1618
)
1719

1820

@@ -142,3 +144,45 @@ def test_yield_chained_docs_root(g):
142144
"d21",
143145
"d211",
144146
]
147+
148+
149+
@pytest.mark.parametrize(
150+
"ops, err",
151+
[
152+
(
153+
[operation(str, "BOOM", provides="BOOM")],
154+
r"Name of provides\('BOOM'\) clashed with a same-named operation",
155+
),
156+
(
157+
[operation(str, "BOOM", needs="BOOM")],
158+
r"Name of operation\(BOOM\) clashed with a same-named dependency",
159+
),
160+
(
161+
[operation(str, "BOOM", provides="a", aliases=("a", "BOOM"))],
162+
r"Name of provides\('BOOM'\) clashed with a same-named operation",
163+
),
164+
(
165+
[operation(str, "op1", provides="BOOM"), operation(str, "BOOM")],
166+
r"Name of operation\(BOOM\) clashed with a same-named dependency",
167+
),
168+
## x2 ops
169+
(
170+
[operation(str, "BOOM"), operation(str, "op2", "BOOM")],
171+
r"Name of needs\('BOOM'\) clashed with a same-named operation",
172+
),
173+
(
174+
[operation(str, "op1", needs="BOOM"), operation(str, "BOOM")],
175+
r"Name of operation\(BOOM\) clashed with a same-named dependency",
176+
),
177+
(
178+
[
179+
operation(str, "op1", provides="a", aliases=("a", "BOOM")),
180+
operation(str, "BOOM"),
181+
],
182+
r"Name of operation\(BOOM\) clashed with a same-named dependency",
183+
),
184+
],
185+
)
186+
def test_node_clashes(ops, err):
187+
with pytest.raises(ValueError, match=err):
188+
Network(*ops)

0 commit comments

Comments
 (0)