pypeit.dashboard.runlock module

The single-run lock for the PypeIt Dashboard.

The lock is scoped to one reduction — the reduction directory the dashboard was opened on — not to the file system or the machine as a whole: at most one PypeIt run (run_pypeit or pypeit_run_to_calibstep) should be active for that reduction at a time, and the Dashboard refuses to launch a second. Reductions in other directories (or other dashboards watching them) are unaffected.

The lock engages in two situations:

  • a run the Dashboard launched is still in progress (driven by the Launcher via RunLock.set_dashboard_running()); and

  • a run started outside the Dashboard is detected by watching the reduction’s log file, <redux_dir>/<pypeit_root>.log (see log_path) — PypeIt writes to it continuously while running, so a log whose modification time is recent means a run is in progress.

The mtime test (RunLock._is_recent()) is a pure, unit-testable function; the polling QTimer only calls it. The lock emits RunLock.lockChanged when it transitions, which the views connect to so they can enable/disable their launch controls.

class pypeit.dashboard.runlock.RunLock(log_path=None, state_path=None, parent=None)[source]

Bases: QObject

Single-run lock for one reduction directory, with log-mtime detection of externally launched runs and a live-monitoring change signal.

The lock is per reduction: it watches the one <redux_dir>/<pypeit_root>.log and <redux_dir>/<pypeit_root>_state.json of the reduction the dashboard was opened on, so it neither sees nor blocks runs in other directories.

The one polling timer serves both roles: it detects whether a run is active (the .log mtime is recent) and, while active, whether the reduction state file has changed (its mtime advanced) — emitting stateChanged so the views can refresh live.

Parameters:
  • log_path (str, pathlib.Path, optional) – The reduction’s <pypeit_root>.log file to watch. If None, only Dashboard-launched runs lock (no external detection).

  • state_path (str, pathlib.Path, optional) – The reduction’s <pypeit_root>_state.json file to watch for live updates. If None, stateChanged never fires.

  • parent (QObject, optional) – The Qt parent.

Variables:
  • lockChanged (Signal) – Emitted with the new locked state (bool) on each transition.

  • stateChanged (Signal) – Emitted (no args) when the state file’s mtime advances while a run is active (live monitoring).

ACTIVE_WINDOW_S = 10.0

A .log modified within this many seconds means a run is active (PypeIt writes to it continuously; a quiet log means it finished or stalled).

POLL_MS = 2500

Polling cadence (ms) for the external-run + state-change check (one timer for both).

_emit_if_changed(was_locked)[source]

Emit lockChanged if the locked state differs from was_locked.

Parameters:

was_locked (bool) – The locked state before the change.

static _is_recent(mtime, now, window)[source]

Whether a file modified at mtime counts as “being written now”.

Pure and unit-testable: True when the log was touched within window seconds of now.

Parameters:
  • mtime (float) – The file modification time (epoch seconds).

  • now (float) – The current time (epoch seconds).

  • window (float) – The “recent” window, in seconds.

Returns:

True if now - mtime < window.

Return type:

bool

_state_mtime()[source]

The state file’s modification time, or None if absent/unreadable.

Returns:

The *_state.json mtime (epoch seconds).

Return type:

float or None

is_locked()[source]

Whether a run is active (Dashboard-launched or external).

Returns:

True if the Dashboard should refuse to launch a run.

Return type:

bool

lockChanged

Emitted with the new locked state (bool) whenever it changes.

poll()[source]

Re-check the .log mtime for an external run (emitting lockChanged on a transition) and the *_state.json mtime for a live update (emitting stateChanged while active).

set_dashboard_running(running)[source]

Record whether a run the Dashboard launched is in progress, emitting lockChanged if the locked state changes.

Parameters:

running (bool) – True while our launched run is active.

start()[source]

Start polling the .log for external runs (and check once now).

stateChanged

Emitted when the state file changes while a run is active.

stop()[source]

Stop polling.