beam: verify_toposort_gen probe for one-shot iterator materialisation
This commit is contained in:
1 parent
25b68eb767
commit
d50b413363
1 file changed
+63
@@ -0,0 +1,63 @@
|
||||
"""Verification: toposort materialises one-shot iterators exactly once.
|
||||
|
||||
Covers the defect Q61 measured: a dependency delivered as a generator must
|
||||
produce the same ordering/cycle result as the equivalent list, including
|
||||
when the graph contains a cycle. Also asserts the iterator is consumed
|
||||
exactly once (call counter).
|
||||
"""
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from toposort import topo_sort
|
||||
|
||||
|
||||
def _check(cond, msg="check failed"):
|
||||
if not cond:
|
||||
raise SystemExit(f"FAIL: {msg}")
|
||||
|
||||
|
||||
class Counter:
|
||||
def __init__(self):
|
||||
self.n = 0
|
||||
|
||||
def gen(self):
|
||||
self.n += 1
|
||||
yield "b"
|
||||
|
||||
|
||||
# 1. generator dep equals list dep (acyclic)
|
||||
c = Counter()
|
||||
r_gen = topo_sort({"a": c.gen()})
|
||||
r_list = topo_sort({"a": ["b"]})
|
||||
_check(r_gen.sorted == r_list.sorted == ["b", "a"],
|
||||
f"acyclic mismatch: gen={r_gen} list={r_list}")
|
||||
# materialised exactly once
|
||||
_check(c.n == 1, f"iterator consumed {c.n} times, expected 1")
|
||||
|
||||
# 2. None deps still work
|
||||
_check(topo_sort({"a": None}).sorted == ["a"])
|
||||
_check(topo_sort({"a": ["z"], "z": None}).sorted == ["z", "a"])
|
||||
|
||||
# 3. cycle delivered as generators must be reported as a cycle (not ordered)
|
||||
def g_ab():
|
||||
yield "b"
|
||||
def g_bc():
|
||||
yield "c"
|
||||
def g_ca():
|
||||
yield "a"
|
||||
r_cyc_gen = topo_sort({"a": g_ab(), "b": g_bc(), "c": g_ca()})
|
||||
r_cyc_list = topo_sort({"a": ["b"], "b": ["c"], "c": ["a"]})
|
||||
_check(r_cyc_gen.sorted is None and r_cyc_list.sorted is None,
|
||||
f"cycle not detected: gen={r_cyc_gen} list={r_cyc_list}")
|
||||
_check(set(r_cyc_gen.cycle) == set(r_cyc_list.cycle) == {"a", "b", "c"},
|
||||
f"cycle mismatch: gen={r_cyc_gen} list={r_cyc_list}")
|
||||
|
||||
# 4. self-loop
|
||||
_check(topo_sort({"a": ["a"]}).cycle == ["a"])
|
||||
|
||||
# 5. multi-form equivalence: filter/map objects
|
||||
r_filter = topo_sort({"a": filter(lambda x: True, ["b"])})
|
||||
r_map = topo_sort({"a": map(str, ["b"])})
|
||||
_check(r_filter.sorted == r_map.sorted == ["b", "a"],
|
||||
f"filter/map mismatch: {r_filter} {r_map}")
|
||||
|
||||
print("ALL VERIFIED")
|
||||
Reference in new issue
Block a user