Compile and graph-check every op before mutating real state. For each op with code: 1. `compile_cell` — validates syntax, extracts defs/refs. 2. Temporarily register in the graph to detect multiply-defined names and cycles. 3. Always clean up so the graph
(self, ops: list[_Op])
| 1540 | # ------------------------------------------------------------------ |
| 1541 | |
| 1542 | def _dry_run_compile(self, ops: list[_Op]) -> None: |
| 1543 | """Compile and graph-check every op before mutating real state. |
| 1544 | |
| 1545 | For each op with code: |
| 1546 | 1. `compile_cell` — validates syntax, extracts defs/refs. |
| 1547 | 2. Temporarily register in the graph to detect multiply-defined |
| 1548 | names and cycles. |
| 1549 | 3. Always clean up so the graph is left unchanged. |
| 1550 | |
| 1551 | Raises `SyntaxError` for invalid code or `RuntimeError` for |
| 1552 | graph conflicts (multiply-defined names, cycles). |
| 1553 | """ |
| 1554 | graph = self.graph |
| 1555 | registered: list[CellId_t] = [] |
| 1556 | # For updates we must evict the old cell first; remember it |
| 1557 | # so we can restore on cleanup. |
| 1558 | evicted: dict[CellId_t, CellImpl] = {} |
| 1559 | |
| 1560 | # Snapshot cell order before any mutations so we can restore it. |
| 1561 | # The evict/re-register cycle appends cells to the end of the |
| 1562 | # dict, which corrupts the ordering that _apply_ops relies on. |
| 1563 | original_order = list(graph.cells.keys()) |
| 1564 | |
| 1565 | # Snapshot existing problems so we only flag *new* ones. |
| 1566 | existing_multiply_defined = set(graph.get_multiply_defined()) |
| 1567 | existing_cycles = set(graph.cycles) |
| 1568 | |
| 1569 | try: |
| 1570 | for op in ops: |
| 1571 | # For deletes, evict the cell so its defs don't conflict |
| 1572 | # with later adds/updates that reuse the same names. |
| 1573 | if isinstance(op, _DeleteOp): |
| 1574 | cell_id = op.cell_id |
| 1575 | if cell_id in graph.cells: |
| 1576 | evicted[cell_id] = graph.cells[cell_id] |
| 1577 | graph.delete_cell(cell_id) |
| 1578 | continue |
| 1579 | |
| 1580 | code: str | None = getattr(op, "code", None) |
| 1581 | if code is None: |
| 1582 | continue |
| 1583 | |
| 1584 | cell_id = op.cell_id |
| 1585 | |
| 1586 | # For updates, temporarily remove the existing cell so the |
| 1587 | # new version can be registered in its place. |
| 1588 | if isinstance(op, _UpdateOp) and cell_id in graph.cells: |
| 1589 | evicted[cell_id] = graph.cells[cell_id] |
| 1590 | graph.delete_cell(cell_id) |
| 1591 | |
| 1592 | cell = compile_cell(code, cell_id=cell_id) |
| 1593 | graph.register_cell(cell_id, cell) |
| 1594 | registered.append(cell_id) |
| 1595 | |
| 1596 | # Only error on problems *introduced* by these ops. |
| 1597 | new_multiply_defined = ( |
| 1598 | set(graph.get_multiply_defined()) - existing_multiply_defined |
| 1599 | ) |
no test coverage detected