"""Job status enumeration and state machine — terminal-state protection.Tracks every job through its lifecycle, from ``CREATED`` to ``COMPLETED``or a terminal failure state. Once a job enters a failure state no furtherstate transitions are allowed."""fromenumimportEnum
[docs]classJobStatus(Enum):"""Lifecycle state for a single batch job. Key values ---------- CREATED Initial state — job has been constructed but not yet started. COMPLETED Terminal success — the full workflow finished without error. PREPARATION_FAILED Terminal failure — the preparation phase could not produce an INP. SIMULATION_FAILED Terminal failure — the Abaqus solver exited with an error. EXTRACTION_FAILED Terminal failure — one or more post-extraction tasks returned ``None``. MONOLITHIC_SCRIPT_FAILED Terminal failure — the monolithic script exited with a non-zero code. JSON_DECODE_ERROR Terminal failure — monolithic or hook script output could not be parsed as JSON. SCRIPT_ERROR Terminal failure — an unhandled exception occurred in a hook or monolithic script. UNKNOWN_ERROR Terminal failure — an exception escaped the worker process. UNKNOWN Fallback value used when no explicit status is available. """CREATED="CREATED"COMPLETED="COMPLETED"PREPARING="PREPARING"PREPARATION_FAILED="PREPARATION_FAILED"PREPARATION_SUCCESS="PREPARATION_SUCCESS"SIMULATING="SIMULATING"SIMULATION_FAILED="SIMULATION_FAILED"SIMULATION_SUCCESS="SIMULATION_SUCCESS"EXTRACTING="EXTRACTING"EXTRACTION_FAILED="EXTRACTION_FAILED"EXTRACTION_SUCCESS="EXTRACTION_SUCCESS"MONOLITHIC_SCRIPT_FAILED="MONOLITHIC_SCRIPT_FAILED"JSON_DECODE_ERROR="JSON_DECODE_ERROR"SCRIPT_ERROR="SCRIPT_ERROR"UNKNOWN_ERROR="UNKNOWN_ERROR"UNKNOWN="UNKNOWN"
# Terminal failure states — once reached, no further state changes allowed (B4)_TERMINAL_FAILURES=frozenset({JobStatus.PREPARATION_FAILED,JobStatus.SIMULATION_FAILED,JobStatus.EXTRACTION_FAILED,JobStatus.MONOLITHIC_SCRIPT_FAILED,JobStatus.JSON_DECODE_ERROR,JobStatus.SCRIPT_ERROR,JobStatus.UNKNOWN_ERROR,})
[docs]classJobStatusManager:"""State machine for a single job with terminal-state protection. The manager tracks one job through its lifecycle. Calling :meth:`record_preparation`, :meth:`record_simulation`, or :meth:`record_extraction` advances the state. Once a terminal failure state is reached, all subsequent transitions are silently ignored — the first failure is the one that is kept. Attributes ---------- error_message : str or None Error message from the first terminal failure, or ``None``. """def__init__(self):self._current_status:JobStatus=JobStatus.CREATEDself._is_successful:bool=Trueself._error_message:str|None=None@propertydeferror_message(self)->str|None:"""Read-only access to the first-failure error message."""returnself._error_messagedef_fail(self,status:JobStatus,msg:str):"""Transition to a terminal failure state (first-failure-wins). If the job is already in a terminal failure state this call is a no-op — only the original failure is preserved. Parameters ---------- status : JobStatus Must be a member of the internal ``_TERMINAL_FAILURES`` set. msg : str Human-readable error description. """ifself._current_statusin_TERMINAL_FAILURES:returnself._is_successful=Falseself._current_status=statusself._error_message=msg
[docs]defrecord_preparation(self,success:bool,error:str=None):"""Record the outcome of the preparation phase. Parameters ---------- success : bool ``True`` if the INP was produced successfully. error : str or None Error message on failure; a default is used if omitted. """ifself._current_statusin_TERMINAL_FAILURES:returnifsuccess:self._current_status=JobStatus.PREPARATION_SUCCESSelse:self._fail(JobStatus.PREPARATION_FAILED,erroror"Preparation step failed.")
[docs]defrecord_simulation(self,success:bool,error:str=None):"""Record the outcome of the Abaqus solver run. Parameters ---------- success : bool ``True`` if the solver exited with code 0. error : str or None Error message on failure; a default is used if omitted. """ifself._current_statusin_TERMINAL_FAILURES:returnifsuccess:self._current_status=JobStatus.SIMULATION_SUCCESSelse:self._fail(JobStatus.SIMULATION_FAILED,erroror"Simulation step failed.")
[docs]defrecord_extraction(self,results:dict):"""Record extraction results; fails if any task returned ``None``. Parameters ---------- results : dict ``{result_name: value}`` mapping. Any ``None`` value triggers ``EXTRACTION_FAILED``. """ifany(visNoneforvinresults.values()):self._fail(JobStatus.EXTRACTION_FAILED,"One or more extraction tasks failed.")
[docs]defget_final_status(self)->JobStatus:"""Return the current state or ``COMPLETED`` if no failure was recorded. Returns ------- JobStatus The terminal failure state if one was reached, otherwise ``JobStatus.COMPLETED``. """ifself._current_statusin_TERMINAL_FAILURES:returnself._current_statusreturnJobStatus.COMPLETED