From d50b4133637511e552a1ecf505fbda3c428fd2b5 Mon Sep 17 00:00:00 2001 From: beam Date: Thu, 29 Oct 2026 22:55:10 +0000 Subject: [PATCH] beam: verify_toposort_gen probe for one-shot iterator materialisation --- wizard-work/beam/verify_toposort_gen.py | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 wizard-work/beam/verify_toposort_gen.py diff --git a/wizard-work/beam/verify_toposort_gen.py b/wizard-work/beam/verify_toposort_gen.py new file mode 100644 index 0000000..a607a93 --- /dev/null +++ b/wizard-work/beam/verify_toposort_gen.py @@ -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")