Build the loop by materializing declarative nodes/edges and building internal nodes. This supports a declarative style where `nodes=[...]` and `edges=[...]` define the loop body. Declarative loop nodes are NodeTemplate blueprints; special handling is still applied for the in
(self)
| 394 | self._terminate_node.reset_gate() |
| 395 | @masf_hook(Node.Hook.BUILD) |
| 396 | def build(self): |
| 397 | """Build the loop by materializing declarative nodes/edges and building internal nodes. |
| 398 | |
| 399 | This supports a declarative style where `nodes=[...]` and `edges=[...]` define the loop |
| 400 | body. Declarative loop nodes are NodeTemplate blueprints; special handling is still applied |
| 401 | for the internal controller/terminate nodes when wiring edges. It then builds the controller |
| 402 | and terminate nodes, and finally calls `BaseGraph.build()`. |
| 403 | |
| 404 | The build is idempotent: calling `build()` multiple times is a no-op after the first |
| 405 | successful build. |
| 406 | """ |
| 407 | if self._is_built: |
| 408 | return |
| 409 | if self._init_nodes: |
| 410 | for item in self._init_nodes: |
| 411 | if len(item) < 2: |
| 412 | raise ValueError(f"Invalid node definition: {item}") |
| 413 | name = item[0] |
| 414 | target = item[1] |
| 415 | others = item[2:] |
| 416 | self.create_node(target, *others, name=name) |
| 417 | |
| 418 | if self._init_edges: |
| 419 | for edge_def in self._init_edges: |
| 420 | if len(edge_def) == 2: |
| 421 | src_name, dst_name = edge_def |
| 422 | keys = None |
| 423 | elif len(edge_def) == 3: |
| 424 | src_name, dst_name, keys = edge_def |
| 425 | else: |
| 426 | raise ValueError(f"Invalid edge definition: {edge_def}") |
| 427 | src_token = str(src_name).strip() |
| 428 | dst_token = str(dst_name).strip() |
| 429 | src_key = src_token.lower() |
| 430 | dst_key = dst_token.lower() |
| 431 | if src_key in ["entry", "controller"]: |
| 432 | if dst_key in ["exit", "controller"]: |
| 433 | dst_node = self._controller |
| 434 | elif dst_key == "terminate": |
| 435 | dst_node = self._terminate_node |
| 436 | else: |
| 437 | dst_node = self._nodes.get(dst_token) |
| 438 | if dst_node is None: |
| 439 | raise ValueError(f"Destination node '{dst_name}' not found in loop '{self.name}'") |
| 440 | |
| 441 | if dst_key == "terminate": |
| 442 | self.edge_to_terminate_node(self._controller, keys) |
| 443 | else: |
| 444 | self.edge_from_controller(dst_node, keys) |
| 445 | else: |
| 446 | src_node = self._nodes.get(src_token) |
| 447 | if src_node is None: |
| 448 | raise ValueError(f"Source node '{src_name}' not found in loop '{self.name}'") |
| 449 | if dst_key in ["exit", "controller"]: |
| 450 | self.edge_to_controller(src_node, keys) |
| 451 | elif dst_key == "terminate": |
| 452 | self.edge_to_terminate_node(src_node, keys) |
| 453 | else: |
nothing calls this directly
no test coverage detected