Validate, plan, format, and apply a batch of operations.
(
self, ops: list[_Op], explicit_run: set[CellId_t] | None = None
)
| 1644 | graph.topology.reorder_nodes(original_order) |
| 1645 | |
| 1646 | async def _apply_ops( |
| 1647 | self, ops: list[_Op], explicit_run: set[CellId_t] | None = None |
| 1648 | ) -> None: |
| 1649 | """Validate, plan, format, and apply a batch of operations.""" |
| 1650 | existing_ids = list(self._document) |
| 1651 | plan = _build_plan(existing_ids, ops) |
| 1652 | |
| 1653 | # Auto-format new/changed code. |
| 1654 | plan = await self._format_plan(plan) |
| 1655 | |
| 1656 | # Diff the plan against the current document. |
| 1657 | existing_id_set = set(self._document) |
| 1658 | existing_code = {cell.id: cell.code for cell in self._document.cells} |
| 1659 | plan_ids = {e.cell_id for e in plan} |
| 1660 | |
| 1661 | # Classify each entry. |
| 1662 | code_entries: list[_PlanEntry] = [] # new or changed code |
| 1663 | config_entries: list[_PlanEntry] = [] # config-only changes |
| 1664 | |
| 1665 | for entry in plan: |
| 1666 | is_new = entry.cell_id not in existing_id_set |
| 1667 | code_changed = ( |
| 1668 | entry.code is not None |
| 1669 | and entry.code != existing_code.get(entry.cell_id) |
| 1670 | ) |
| 1671 | if is_new or code_changed: |
| 1672 | code_entries.append(entry) |
| 1673 | elif entry.config is not None: |
| 1674 | config_entries.append(entry) |
| 1675 | |
| 1676 | # Resolve configs for all entries (code and config-only). |
| 1677 | resolved_configs: dict[CellId_t, CellConfig] = {} |
| 1678 | for entry in code_entries + config_entries: |
| 1679 | if entry.config is not None: |
| 1680 | resolved_configs[entry.cell_id] = entry.config |
| 1681 | elif entry.cell_id not in existing_id_set: |
| 1682 | resolved_configs[entry.cell_id] = CellConfig(hide_code=True) |
| 1683 | else: |
| 1684 | existing_meta = self._kernel.cell_metadata.get(entry.cell_id) |
| 1685 | resolved_configs[entry.cell_id] = ( |
| 1686 | existing_meta.config if existing_meta else CellConfig() |
| 1687 | ) |
| 1688 | |
| 1689 | # Let mutate_graph handle all graph mutations: it properly |
| 1690 | # cleans up globals, UI elements, and lifecycle hooks for |
| 1691 | # deleted/replaced cells via _delete_cell / _deactivate_cell. |
| 1692 | execution_requests = [ |
| 1693 | ExecuteCellCommand(cell_id=e.cell_id, code=e.code) |
| 1694 | for e in code_entries |
| 1695 | if e.code is not None |
| 1696 | ] |
| 1697 | deletion_requests = [ |
| 1698 | DeleteCellCommand(cell_id=cid) |
| 1699 | for cid in existing_id_set - plan_ids |
| 1700 | if cid in self.graph.cells |
| 1701 | ] |
| 1702 | cells_to_run = self._kernel.mutate_graph( |
| 1703 | execution_requests, deletion_requests |
no test coverage detected