Source code for matmelt.commands.status

"""The ``status`` command: a read-only view of the run's durable state (5.1e).

Opens the state database under ``run.work_dir`` (WAL mode -- safe to read
while a ``run`` is live in another terminal) and reports the per-state
counts, every terminally-failed material with the stage and reason it died
on, and the in-flight job ledger. It changes nothing and never claims.

``--state <state>`` switches to the picking-list view: the material ids
currently sitting at that state (with their parents), i.e. exactly what a
user browses to build a ``run --materials`` selection.

Exit codes: 0 = state shown (failed materials are *content*, not a command
failure), 1 = no run state exists yet, 2 = config error / unknown state name.
"""

from __future__ import annotations

from pathlib import Path

from matmelt.commands import STATE_DB_FILENAME, resolve_config_path
from matmelt.config import ConfigError, load_config
from matmelt.core.state import MaterialState, StateStore
from matmelt.ui.console import (
    print_json_payload,
    render_config_error,
    render_error,
    render_state_materials,
    render_status,
)


[docs] def run_status(config_path: str, *, as_json: bool = False, state: str | None = None) -> int: """Show what the run's state database currently records. Parameters ---------- config_path The config file naming the workspace to read. as_json Emit a JSON payload on stdout instead of Rich tables. state List the material ids sitting at one lifecycle state, instead of the tally. Case-insensitive. This is the picking list for ``run --materials`` and ``retry``. Returns ------- int ``0`` on success, ``1`` when there is no run state yet, ``2`` for a config error or an unknown state name. Notes ----- Read-only: the database is byte-identical afterwards, and WAL mode makes this safe to run while a job is in flight. """ config_path = resolve_config_path(config_path) try: config = load_config(config_path) except ConfigError as exc: render_config_error(str(exc)) return 2 wanted: MaterialState | None = None if state is not None: try: wanted = MaterialState(state.strip().upper()) except ValueError: valid = ", ".join(member.value for member in MaterialState) render_error(f"status: unknown state {state!r}; valid states: {valid}") return 2 db_path = Path(config.run.work_dir) / STATE_DB_FILENAME if not db_path.is_file(): render_error(f"status: no run state at {db_path} -- start one with 'matmelt run'.") return 1 if wanted is not None: return _list_state(config.run.name, db_path, wanted, as_json=as_json) store = StateStore(db_path) try: counts = store.count_by_state() failed = store.query({"state": MaterialState.FAILED}) jobs = store.list_jobs() finally: store.close() if as_json: print_json_payload( { "run": config.run.name, "counts": {state.value: count for state, count in counts.items()}, "failed": [ { "id": record.material_id, "stage": record.fail_stage, "reason": record.fail_reason, "attempts": record.attempts, } for record in failed ], "in_flight": [ { "job_key": job.job_key, "stage": job.stage, "backend": job.backend_name, "materials": list(job.material_ids), } for job in jobs ], } ) else: render_status(config.run.name, counts, failed, jobs) return 0
def _list_state(run_name: str, db_path: Path, state: MaterialState, *, as_json: bool) -> int: """List the material ids at one state -- the ``--state`` leg. Parameters ---------- run_name Shown in the header. db_path The state database. state Which state to list. as_json Emit a JSON payload instead of a table. Returns ------- int ``0``. An empty state is a normal answer, not a failure. """ store = StateStore(db_path) try: records = store.query({"state": state}) finally: store.close() if as_json: print_json_payload( { "run": run_name, "state": state.value, "materials": [ {"id": record.material_id, "parent_id": record.parent_id} for record in records ], } ) else: render_state_materials(run_name, state, records) return 0