Source code for adaptive_scheduler._server_support.multi_run_manager

from __future__ import annotations

from typing import TYPE_CHECKING

import ipywidgets as ipw
from IPython.display import display

from adaptive_scheduler.widgets import _disable_widgets_output_scrollbar, info

if TYPE_CHECKING:
    from adaptive_scheduler._server_support.run_manager import RunManager


[docs] class MultiRunManager: """A manager that can contain multiple RunManagers. Parameters ---------- run_managers Initial list of RunManagers to include. Attributes ---------- run_managers Dictionary of managed RunManagers, keyed by their names. """ def __init__(self, run_managers: list[RunManager] | None = None) -> None: self.run_managers: dict[str, RunManager] = {} self._widget: ipw.VBox | None = None self._tab_widget: ipw.Tab | None = None self._info_widgets: dict[str, ipw.Widget] = {} self._update_all_button: ipw.Button | None = None if run_managers: for rm in run_managers: self.add_run_manager(rm)
[docs] def add_run_manager( self, run_manager: RunManager, *, start: bool = False, wait_for: str | None = None, ) -> None: """Add a new RunManager to the MultiRunManager. Parameters ---------- run_manager The RunManager to add. start Whether to start the RunManager immediately after adding it. wait_for The name of another RunManager to wait for before starting this one. Only applicable if start is True. Raises ------ ValueError If a RunManager with the same name already exists. KeyError If the specified wait_for RunManager does not exist. """ if run_manager.job_name in self.run_managers: msg = f"A RunManager with the name '{run_manager.job_name}' already exists." raise ValueError(msg) self.run_managers[run_manager.job_name] = run_manager self._info_widgets[run_manager.job_name] = info( run_manager, display_widget=False, disable_widgets_output_scrollbar=False, ) if start: if wait_for: if wait_for not in self.run_managers: msg = f"No RunManager with the name '{wait_for}' exists." raise KeyError(msg) run_manager.start(wait_for=self.run_managers[wait_for]) else: run_manager.start() elif wait_for: msg = "`start` must be True if `wait_for` is used." raise ValueError(msg) if self._widget is not None: self._update_widget()
[docs] def remove_run_manager(self, name: str) -> None: """Remove a RunManager from the MultiRunManager. Parameters ---------- name The name of the RunManager to remove. Raises ------ KeyError If no RunManager with the given name exists. """ if name in self.run_managers: rm = self.run_managers.pop(name) rm.cancel() self._info_widgets.pop(name) if self._widget is not None: self._update_widget() else: msg = f"No RunManager with the name '{name}' exists." raise KeyError(msg)
[docs] def start_all(self) -> None: """Start all RunManagers.""" for run_manager in self.run_managers.values(): run_manager.start()
[docs] def cancel_all(self) -> None: """Cancel all RunManagers.""" for run_manager in self.run_managers.values(): run_manager.cancel()
def _create_widget(self) -> ipw.VBox: """Create the widget for displaying RunManager info and update button.""" self._tab_widget = ipw.Tab() children = list(self._info_widgets.values()) self._tab_widget.children = children for i, name in enumerate(self.run_managers.keys()): self._tab_widget.set_title(i, name) self._update_all_button = ipw.Button( description="Update All", button_style="info", tooltip="Update all RunManagers", ) self._update_all_button.on_click(self._update_all_callback) return ipw.VBox([self._update_all_button, self._tab_widget]) def _update_widget(self) -> None: """Update the widget when RunManagers are added or removed.""" if self._tab_widget is not None: current_children = list(self._tab_widget.children) new_children = list(self._info_widgets.values()) # Create a new tuple of children updated_children = tuple(child for child in current_children if child in new_children) # Add new children for widget in new_children: if widget not in updated_children: updated_children += (widget,) # Update the widget's children self._tab_widget.children = updated_children # Update titles for i, name in enumerate(self.run_managers.keys()): self._tab_widget.set_title(i, name) def _update_all_callback(self, _: ipw.Button) -> None: """Callback function for the Update All button.""" assert self._tab_widget is not None for widget in self._tab_widget.children: update_button = widget.children[0].children[1].children[0] assert update_button.description == "update info" update_button.click()
[docs] def info(self) -> ipw.VBox: """Display info about all RunManagers in a widget with an Update All button.""" if self._widget is None: _disable_widgets_output_scrollbar() self._widget = self._create_widget() return self._widget
def _repr_html_(self) -> str: """HTML representation for Jupyter notebooks.""" return self.info()
[docs] def display(self) -> None: """Display the widget.""" display(self.info())