Skip to content

Commit fa44ed8

Browse files
committed
Refactor names and comments
1 parent 95cfcda commit fa44ed8

8 files changed

Lines changed: 56 additions & 81 deletions

File tree

docs/ert/reference/workflows/complete_workflows.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ Observe that the workflows being 'hooked in' with the
8888
:code:`HOOK_WORKFLOW` must be loaded with the :code:`LOAD_WORKFLOW`
8989
keyword.
9090

91+
Workflow output
92+
-----------------
93+
9194
Output from workflow jobs is written to the ERT log. Every job invocation gets an
9295
entry holding whatever the job wrote to stdout and stderr::
9396

@@ -105,9 +108,14 @@ Workflows hooked in with :code:`HOOK_WORKFLOW` are in addition recorded
105108
alongside the experiment they belong to, in
106109
:code:`<ENSPATH>/experiments/<experiment_id>/workflow_events.jsonl`. That file
107110
holds one JSON object per job invocation and exists so the GUI can show the
108-
output of a workflow again later; it is not meant to be read directly. Output
109-
from hooks that run before the experiment is created, such as
110-
:code:`PRE_EXPERIMENT`, is held back and written once the experiment exists.
111+
output of a workflow again later; it is not meant to be read directly. To view this
112+
in the GUI, select the experiment in the *Experiments* tool, then select the
113+
*Workflow events* tab.
114+
115+
The GUI will show a list of all workflow jobs that have
116+
been run for that experiment, and clicking on a job will show its output.
117+
Output from hooks that run before the experiment is created, such as
118+
:code:`PRE_EXPERIMENT`, is held back and written once the storage is created.
111119

112120
.. _runpath-file-workflows:
113121

src/ert/config/ert_script.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ def _capturing(stream_name: str) -> Iterator[io.StringIO]:
153153
class ExternalScriptError(RuntimeError):
154154
"""Raised when an external workflow job exits with a non-zero exit code.
155155
156-
Reported without a stack trace, since the trace would only show the ert
157-
internals that started the job, and not what went wrong inside it.
156+
Reported without a stack trace, since it would only
157+
show ert internals and could be confusing
158158
"""
159159

160160

src/ert/run_models/event.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ def write_as_csv(self, output_path: Path | None) -> None:
9393
self.data.to_csv("Report", output_path / str(self.run_id))
9494

9595

96-
class RunModelWorkflowLogEvent(BaseModel, extra="forbid"):
96+
class WorkflowEvent(BaseModel, extra="forbid"):
9797
"""The output of a single workflow job invocation."""
9898

99-
event_type: Literal["RunModelWorkflowLogEvent"] = "RunModelWorkflowLogEvent"
99+
event_type: Literal["WorkflowEvent"] = "WorkflowEvent"
100100
run_id: UUID
101101
hook: str
102102
workflow_name: str
@@ -144,10 +144,10 @@ class RunPathCreatedEvent(RunPathCreationEvent):
144144
| RunModelTimeEvent
145145
| RunModelUpdateBeginEvent
146146
| RunModelUpdateEndEvent
147-
| RunModelWorkflowLogEvent
148147
| SnapshotUpdateEvent
149148
| StartEvent
150149
| WarningEvent
150+
| WorkflowEvent
151151
| EnsembleEvaluationWarning
152152
| StartingTotalRunPathCreationEvent
153153
| FinishedTotalRunPathCreationEvent

src/ert/run_models/run_model.py

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@
8181
from .event import (
8282
EndEvent,
8383
FullSnapshotEvent,
84-
RunModelWorkflowLogEvent,
8584
SnapshotUpdateEvent,
8685
StartEvent,
8786
StatusEvents,
87+
WorkflowEvent,
8888
)
8989

9090
if TYPE_CHECKING:
@@ -180,10 +180,8 @@ class RunModel(RunModelConfig, ABC):
180180
_start_iteration: int = PrivateAttr(default=0)
181181
_max_parallelism_violation: ParallelismViolation = ParallelismViolation()
182182
_workflow_runner: WorkflowRunner | None = PrivateAttr(default=None)
183-
_workflow_log_id: uuid.UUID = PrivateAttr(default_factory=uuid.uuid4)
184-
_pending_workflow_events: list[RunModelWorkflowLogEvent] = PrivateAttr(
185-
default_factory=list
186-
)
183+
_workflow_run_id: uuid.UUID = PrivateAttr(default_factory=uuid.uuid4)
184+
_pending_workflow_events: list[WorkflowEvent] = PrivateAttr(default_factory=list)
187185

188186
def __init__(
189187
self,
@@ -386,7 +384,7 @@ def handle_captured_event(message: Warning | str) -> None:
386384
self.send_event(WarningEvent(msg=str(message)))
387385

388386
start_timestamp = datetime.datetime.now(tz=datetime.UTC)
389-
self._workflow_log_id = uuid.uuid4()
387+
self._workflow_run_id = uuid.uuid4()
390388
self._pending_workflow_events = []
391389
try: # ruff: ignore[too-many-statements-in-try-clause]
392390
self.send_event(StartEvent(timestamp=start_timestamp))
@@ -847,11 +845,8 @@ def run_workflows(
847845
try:
848846
for workflow in self.hooked_workflows[fixtures.hook]:
849847
if self._end_event.is_set():
850-
# The experiment was already cancelled before this
851-
# workflow got a chance to start. Report every one of
852-
# its jobs as cancelled so it still shows up in the
853-
# workflow log, instead of disappearing silently.
854-
self._send_cancelled_workflow_log_events(
848+
# Cancel all remaining workflows
849+
self._send_cancelled_workflow_events(
855850
workflow=workflow,
856851
hook=fixtures.hook,
857852
iteration=iteration,
@@ -868,28 +863,28 @@ def run_workflows(
868863
workflow_runner.run_blocking()
869864
finally:
870865
self._workflow_runner = None
871-
self._send_workflow_log_events(
866+
self._send_workflow_events(
872867
workflow_runner=workflow_runner,
873868
hook=fixtures.hook,
874869
workflow_name=workflow.name,
875870
iteration=iteration,
876871
)
877872
finally:
878-
self._persist_workflow_log(experiment)
873+
self._persist_workflow_events_to_storage(experiment)
879874

880875
if self._end_event.is_set():
881876
raise UserCancelled("Experiment cancelled by user during workflows")
882877

883-
def _send_workflow_log_events(
878+
def _send_workflow_events(
884879
self,
885880
workflow_runner: WorkflowRunner,
886881
hook: HookRuntime,
887882
workflow_name: str,
888883
iteration: int | None,
889884
) -> None:
890885
events = [
891-
RunModelWorkflowLogEvent(
892-
run_id=self._workflow_log_id,
886+
WorkflowEvent(
887+
run_id=self._workflow_run_id,
893888
hook=str(hook),
894889
workflow_name=workflow_name,
895890
job_name=result.name,
@@ -908,20 +903,17 @@ def _send_workflow_log_events(
908903
self.send_event(event)
909904
self._pending_workflow_events.extend(events)
910905

911-
def _send_cancelled_workflow_log_events(
906+
def _send_cancelled_workflow_events(
912907
self,
913908
workflow: Workflow,
914909
hook: HookRuntime,
915910
iteration: int | None,
916911
) -> None:
917-
"""Report every job in a hooked workflow that never got a chance to
918-
start because the experiment was already cancelled, so it still
919-
shows up in the workflow log instead of disappearing silently.
920-
"""
912+
# Report jobs not started due to cancellation
921913
now = datetime.datetime.now(tz=datetime.UTC)
922914
events = [
923-
RunModelWorkflowLogEvent(
924-
run_id=self._workflow_log_id,
915+
WorkflowEvent(
916+
run_id=self._workflow_run_id,
925917
hook=str(hook),
926918
workflow_name=workflow.name,
927919
job_name=job.name,
@@ -940,18 +932,18 @@ def _send_cancelled_workflow_log_events(
940932
self.send_event(event)
941933
self._pending_workflow_events.extend(events)
942934

943-
def _persist_workflow_log(self, experiment: Experiment | None) -> None:
944-
"""Output from hooks that run before the experiment exists in storage,
945-
such as PRE_EXPERIMENT, is held back until an experiment is available.
946-
"""
935+
def _persist_workflow_events_to_storage(
936+
self, experiment: Experiment | None
937+
) -> None:
938+
# Hold back output until storage is created
947939
if experiment is None or not self._pending_workflow_events:
948940
return
949941
try:
950942
experiment.append_workflow_events(
951943
event.model_dump_json() for event in self._pending_workflow_events
952944
)
953945
except Exception:
954-
logger.exception("Failed to persist workflow log to storage")
946+
logger.exception("Failed to persist workflow events to storage")
955947
self._pending_workflow_events = []
956948

957949
def _evaluate_and_postprocess(

src/ert/workflow_runner.py

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121

2222
@dataclass
2323
class WorkflowJobResult:
24-
"""The outcome of a single invocation of a workflow job."""
25-
2624
name: str
2725
index: int
2826
arguments: list[str]
@@ -43,14 +41,6 @@ def status(self) -> str:
4341
return "success"
4442

4543
def as_log_entry(self, workflow_name: str, hook: str | None = None) -> str:
46-
"""A multi-line rendering of this result for the ERT log.
47-
48-
Args:
49-
workflow_name: The workflow the job was invoked from.
50-
hook: The runtime the workflow was hooked to, if any. Workflows
51-
started from the command line or the GUI's Run workflow tool
52-
are not hooked to anything.
53-
"""
5444
header = "Workflow job"
5545
if hook is not None:
5646
header += f" {hook}"
@@ -160,14 +150,6 @@ def __init__(
160150
fixtures: WorkflowFixtures,
161151
hook: str | None = None,
162152
) -> None:
163-
"""
164-
Args:
165-
workflow: The workflow to run.
166-
fixtures: The fixtures made available to the workflow's jobs.
167-
hook: The runtime the workflow was hooked to, used to identify it
168-
in the log. Workflows started from the command line or the
169-
GUI's Run workflow tool are not hooked to anything.
170-
"""
171153
self.__workflow = workflow
172154
self.fixtures = fixtures
173155
self._hook = hook
@@ -211,9 +193,7 @@ def run_blocking(self) -> None:
211193

212194
for index, (job, args) in enumerate(self.__workflow):
213195
if self.__cancelled:
214-
# The workflow was already cancelled before this job got a
215-
# chance to start - record it as cancelled rather than
216-
# silently dropping it.
196+
# The workflow was cancelled before this job started
217197
result = WorkflowJobResult(
218198
name=job.name,
219199
index=index,
@@ -232,10 +212,7 @@ def run_blocking(self) -> None:
232212
logger.info(f"Workflow job {jobrunner.name} starting")
233213
jobrunner.run(args, fixtures=self.fixtures)
234214
job_was_cancelled = self.__cancelled
235-
# A job that was interrupted by a cancellation request did not
236-
# complete, so it is reported as failed rather than succeeded -
237-
# cooperatively cancelled internal jobs otherwise return
238-
# normally and hasFailed() would stay False.
215+
239216
failed = jobrunner.hasFailed() or job_was_cancelled
240217
self.__status[jobrunner.name] = {
241218
"stdout": jobrunner.stdoutdata(),
@@ -253,8 +230,6 @@ def run_blocking(self) -> None:
253230
self.__job_results.append(result)
254231

255232
extra = self._log_extra(result, execution_type=jobrunner.execution_type)
256-
# Logged before acting on stop_on_fail, so the output of the job
257-
# that aborted the workflow is in the log too.
258233
if failed and not job_was_cancelled:
259234
logger.error(self._log_entry(result), extra=extra)
260235
else:

tests/ert/ui_tests/cli/test_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
ES_MDA_MODE,
3131
TEST_RUN_MODE,
3232
)
33-
from ert.run_models.event import RunModelWorkflowLogEvent
33+
from ert.run_models.event import WorkflowEvent
3434
from ert.sample_prior import sample_prior
3535
from ert.scheduler.driver import Driver
3636
from ert.scheduler.job import Job
@@ -546,7 +546,7 @@ def test_that_workflow_output_is_written_to_the_experiment_in_storage():
546546
(line,) = experiment.workflow_events_path.read_text(
547547
encoding="utf-8"
548548
).splitlines()
549-
event = RunModelWorkflowLogEvent.model_validate_json(line)
549+
event = WorkflowEvent.model_validate_json(line)
550550
assert event.hook == "PRE_SIMULATION"
551551
assert event.workflow_name == "wfprint"
552552
assert event.job_name == "printjob"

tests/ert/unit_tests/run_models/test_base_run_model.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from ert.mode_definitions import TEST_RUN_MODE
3939
from ert.plugins import ErtRuntimePlugins
4040
from ert.run_models import create_model
41-
from ert.run_models.event import RunModelWorkflowLogEvent
41+
from ert.run_models.event import WorkflowEvent
4242
from ert.run_models.run_model import (
4343
RunModel,
4444
UserCancelled,
@@ -899,11 +899,11 @@ def _drain(status_queue):
899899

900900

901901
def _persisted_workflow_events(experiment):
902-
"""The workflow log events stored for an experiment, oldest first."""
902+
"""The workflow events persisted to storage for an experiment, oldest first."""
903903
if not experiment.workflow_events_path.exists():
904904
return []
905905
return [
906-
RunModelWorkflowLogEvent.model_validate_json(line)
906+
WorkflowEvent.model_validate_json(line)
907907
for line in experiment.workflow_events_path.read_text(
908908
encoding="utf-8"
909909
).splitlines()
@@ -929,7 +929,7 @@ def _printing_workflow(tmp_path, name, script, *, stop_on_fail=False):
929929
)
930930

931931

932-
def test_that_run_workflows_sends_a_workflow_log_event_per_job(tmp_path, use_tmpdir):
932+
def test_that_run_workflows_sends_a_workflow_event_per_job(tmp_path, use_tmpdir):
933933
workflow = _printing_workflow(tmp_path, "hello", 'print("hello from workflow")')
934934
workflow.cmd_list.append(workflow.cmd_list[0])
935935
status_queue = SimpleQueue()
@@ -946,11 +946,11 @@ def test_that_run_workflows_sends_a_workflow_log_event_per_job(tmp_path, use_tmp
946946
("HELLO", 1, "hello from workflow\n"),
947947
]
948948
assert all(e.hook == "PRE_EXPERIMENT" for e in events)
949-
assert all(e.run_id == brm._workflow_log_id for e in events)
949+
assert all(e.run_id == brm._workflow_run_id for e in events)
950950
assert not any(e.failed for e in events)
951951

952952

953-
def test_that_a_workflow_log_event_is_sent_when_stop_on_fail_aborts_the_workflow(
953+
def test_that_a_workflow_event_is_sent_when_stop_on_fail_aborts_the_workflow(
954954
tmp_path, use_tmpdir
955955
):
956956
workflow = _printing_workflow(
@@ -1009,7 +1009,7 @@ def test_that_a_cancelled_job_and_its_unstarted_siblings_carry_the_workflow_name
10091009
),
10101010
]
10111011

1012-
brm._send_workflow_log_events(
1012+
brm._send_workflow_events(
10131013
workflow_runner=workflow_runner,
10141014
hook=HookRuntime.PRE_SIMULATION,
10151015
workflow_name="my_workflow",
@@ -1026,7 +1026,7 @@ def test_that_a_cancelled_job_and_its_unstarted_siblings_carry_the_workflow_name
10261026
assert never_started_event.cancelled
10271027

10281028

1029-
def test_that_workflow_log_events_from_an_update_hook_carry_the_iteration(
1029+
def test_that_workflow_events_from_an_update_hook_carry_the_iteration(
10301030
tmp_path, use_tmpdir
10311031
):
10321032
workflow = _printing_workflow(tmp_path, "hello", 'print("hello")')
@@ -1119,7 +1119,7 @@ def test_that_pre_experiment_output_is_persisted_once_an_experiment_exists(
11191119
]
11201120

11211121

1122-
def test_that_a_failure_to_persist_the_workflow_log_does_not_stop_the_experiment(
1122+
def test_that_a_failure_to_persist_workflow_events_does_not_stop_the_experiment(
11231123
tmp_path, use_tmpdir, caplog
11241124
):
11251125
workflow = _printing_workflow(tmp_path, "hello", 'print("hello from workflow")')
@@ -1143,7 +1143,7 @@ def test_that_a_failure_to_persist_the_workflow_log_does_not_stop_the_experiment
11431143
)
11441144
)
11451145

1146-
assert "Failed to persist workflow log" in caplog.text
1146+
assert "Failed to persist workflow events to storage" in caplog.text
11471147
assert _drain(status_queue), "the event should still be sent"
11481148

11491149

@@ -1181,14 +1181,14 @@ def test_that_starting_an_experiment_discards_workflow_output_from_the_previous_
11811181
brm = create_run_model()
11821182
brm._status_queue = SimpleQueue()
11831183
brm._pending_workflow_events = [MagicMock()]
1184-
previous_log_id = brm._workflow_log_id
1184+
previous_log_id = brm._workflow_run_id
11851185

11861186
brm.start_simulations_thread(
11871187
EvaluatorServerConfig(use_token=False), rerun_failed_realizations=True
11881188
)
11891189

11901190
assert brm._pending_workflow_events == []
1191-
assert brm._workflow_log_id != previous_log_id
1191+
assert brm._workflow_run_id != previous_log_id
11921192

11931193

11941194
def test_that_workflow_output_is_persisted_when_stop_on_fail_aborts_the_workflow(
@@ -1266,7 +1266,7 @@ def test_that_workflows_hooked_after_a_cancelled_one_still_appear_as_cancelled(
12661266
"""Regression test: when several workflows are hooked to the same
12671267
runtime and cancellation happens while the first one is running, the
12681268
workflows that come after it in the hook's list must still be reported
1269-
(as cancelled) rather than silently disappearing from the workflow log.
1269+
(as cancelled) rather than silently disappearing from the workflow events.
12701270
"""
12711271
first = _printing_workflow(tmp_path, "first", 'print("first workflow")')
12721272
second = _printing_workflow(tmp_path, "second", 'print("second workflow")')

0 commit comments

Comments
 (0)