Skip to content

Commit 78a1597

Browse files
test: support plural subgraphs: form, pull spec v0.8.1
Bump the spec submodule from v0.8.0 to v0.8.1 to pick up fixture 019 (subgraph-two-level-nesting), the regression coverage that exercises namespace and parent_states stacking at depth 2. Extend the conformance harness to handle the plural `subgraphs:` map at a fixture's top level. Subgraphs there can reference each other, so they're compiled in dependency order via iterative resolution — each pass picks up subgraphs whose referenced names are already in the registry, and the loop errors on cycles or unresolvable names. Singular `subgraph:` handling (used by 006, 011, 013) is unchanged. Engine itself needs no changes; it already supports arbitrary nesting depth through descend_into_subgraph recursion. Fixture 019 passes on the first run after the harness extension.
1 parent c332657 commit 78a1597

2 files changed

Lines changed: 56 additions & 3 deletions

File tree

tests/conformance/test_conformance.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,55 @@ def scan(graph: Any) -> str | None:
103103
return None
104104

105105

106+
def _subgraph_dependencies(sub_spec: dict[str, Any]) -> set[str]:
107+
"""Names of subgraphs referenced by `subgraph: <name>` directives in
108+
this subgraph's nodes. Used to order plural-form compilation so an
109+
inner subgraph compiles before any subgraph that references it."""
110+
deps: set[str] = set()
111+
nodes = sub_spec.get("nodes")
112+
if not isinstance(nodes, dict):
113+
return deps
114+
for node_spec in cast("dict[str, Any]", nodes).values():
115+
if isinstance(node_spec, dict) and "subgraph" in cast("dict[str, Any]", node_spec):
116+
ref = cast("dict[str, Any]", node_spec)["subgraph"]
117+
if isinstance(ref, str):
118+
deps.add(ref)
119+
return deps
120+
121+
122+
def _compile_subgraphs_map(
123+
subgraphs_spec: dict[str, dict[str, Any]],
124+
registry: dict[str, Any],
125+
) -> None:
126+
"""Compile the plural `subgraphs:` map into ``registry``.
127+
128+
Iterates until every subgraph is compiled, picking entries whose
129+
referenced subgraphs are already in the registry. This keeps the
130+
fixture format order-independent — fixture 019 happens to list inner
131+
before middle, but the harness shouldn't depend on that.
132+
"""
133+
pending: dict[str, dict[str, Any]] = dict(subgraphs_spec)
134+
while pending:
135+
progress = False
136+
for name, sub_spec in list(pending.items()):
137+
deps = _subgraph_dependencies(sub_spec)
138+
if not deps.issubset(registry):
139+
continue
140+
# Plural form omits the `name:` field (the dict key IS the name);
141+
# synthesize it for build_graph's existing singular-form lookup.
142+
sub_with_name = {**sub_spec, "name": name}
143+
sub_built = build_graph(
144+
sub_with_name,
145+
subgraphs=registry,
146+
model_name=f"{name.title()}State",
147+
)
148+
registry[name] = sub_built.builder.compile()
149+
del pending[name]
150+
progress = True
151+
if not progress:
152+
raise RuntimeError(f"unresolvable subgraph dependencies in subgraphs: map: {sorted(pending)}")
153+
154+
106155
@pytest.mark.parametrize("fixture_path", _STANDARD_RUNTIME_FIXTURES, ids=_fixture_id)
107156
async def test_runtime_fixture(fixture_path: Path) -> None:
108157
spec = _load(fixture_path)
@@ -113,13 +162,17 @@ async def test_runtime_fixture(fixture_path: Path) -> None:
113162
if (hit := _unsupported_directive(spec)) is not None:
114163
pytest.skip(f"{fixture_path.stem}: unsupported node directive {hit}")
115164

116-
# Subgraph fixtures (006, 011, 013) declare an inner subgraph that the
117-
# outer graph references by name.
165+
# Subgraph fixtures (006, 011, 013) declare an inner subgraph via the
166+
# singular `subgraph:` key. Fixture 019 introduces the plural `subgraphs:`
167+
# map for two-level nesting; subgraphs there can reference each other,
168+
# so they're compiled in dependency order.
118169
subgraphs: dict[str, Any] = {}
119170
if "subgraph" in spec:
120171
sub_spec = spec["subgraph"]
121172
sub_built = build_graph(sub_spec, model_name=f"{sub_spec['name'].title()}State")
122173
subgraphs[sub_spec["name"]] = sub_built.builder.compile()
174+
if "subgraphs" in spec:
175+
_compile_subgraphs_map(spec["subgraphs"], subgraphs)
123176

124177
built = build_graph(spec, subgraphs=subgraphs)
125178
compiled = built.builder.compile()

0 commit comments

Comments
 (0)