Skip to content

Commit a0ee209

Browse files
committed
Ensure workflow status shows the earliest of the stop point, hold point or stop task
1 parent 47b74ae commit a0ee209

File tree

2 files changed

+82
-21
lines changed

2 files changed

+82
-21
lines changed

cylc/flow/workflow_status.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
"""Workflow status constants."""
1717

1818
from enum import Enum
19-
from typing import TYPE_CHECKING
19+
from typing import TYPE_CHECKING, Optional, Union
2020

21+
from cylc.flow.cycling.loader import get_point
22+
from cylc.flow.id import tokenise
2123
from cylc.flow.wallclock import get_time_string_from_unix_time as time2str
2224

2325
if TYPE_CHECKING:
2426
from optparse import Values
27+
28+
from cylc.flow.cycling import PointBase
2529
from cylc.flow.scheduler import Scheduler
30+
from cylc.flow.task_pool import TaskPool
2631

2732
# Keys for identify API call
2833
KEY_GROUP = "group"
@@ -160,26 +165,45 @@ def get_workflow_status_msg(schd: 'Scheduler') -> str:
160165
return f'reloading: {schd.reload_pending}'
161166
if schd.is_stalled:
162167
if schd.is_paused:
163-
return 'stalled (paused)'
168+
return 'stalled and paused'
164169
return 'stalled'
165170
if schd.is_paused:
166171
return 'paused'
167-
if schd.pool.hold_point:
168-
return WORKFLOW_STATUS_RUNNING_TO_HOLD % schd.pool.hold_point
169-
if schd.pool.stop_point:
170-
return WORKFLOW_STATUS_RUNNING_TO_STOP % schd.pool.stop_point
171172
if schd.stop_clock_time is not None:
172173
return WORKFLOW_STATUS_RUNNING_TO_STOP % time2str(
173174
schd.stop_clock_time
174175
)
175-
if schd.pool.stop_task_id:
176-
return WORKFLOW_STATUS_RUNNING_TO_STOP % schd.pool.stop_task_id
176+
stop_point_msg = _get_earliest_stop_point_status_msg(schd.pool)
177+
if stop_point_msg is not None:
178+
return stop_point_msg
177179
if schd.config and schd.config.final_point:
178180
return WORKFLOW_STATUS_RUNNING_TO_STOP % schd.config.final_point
179181
# fallback - running indefinitely
180182
return 'running'
181183

182184

185+
def _get_earliest_stop_point_status_msg(pool: 'TaskPool') -> Optional[str]:
186+
"""Return the status message for the earliest stop point in the pool,
187+
if any."""
188+
template = WORKFLOW_STATUS_RUNNING_TO_STOP
189+
prop: Union[PointBase, str, None] = pool.stop_task_id
190+
min_point: Optional[PointBase] = get_point(
191+
tokenise(pool.stop_task_id, relative=True)['cycle']
192+
if pool.stop_task_id else None
193+
)
194+
for point, tmpl in (
195+
(pool.stop_point, WORKFLOW_STATUS_RUNNING_TO_STOP),
196+
(pool.hold_point, WORKFLOW_STATUS_RUNNING_TO_HOLD)
197+
):
198+
if point is not None and (min_point is None or point < min_point):
199+
template = tmpl
200+
min_point = point
201+
prop = point
202+
if prop is None:
203+
return None
204+
return template % prop
205+
206+
183207
class RunMode:
184208
"""The possible run modes of a workflow."""
185209

tests/unit/test_workflow_status.py

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@
1919
import pytest
2020
from metomi.isodatetime.data import TimePoint
2121

22+
from cylc.flow.cycling.integer import IntegerPoint
2223
from cylc.flow.workflow_status import (
23-
StopMode,
24-
WorkflowStatus,
2524
WORKFLOW_STATUS_RUNNING_TO_HOLD,
2625
WORKFLOW_STATUS_RUNNING_TO_STOP,
26+
StopMode,
27+
WorkflowStatus,
2728
get_workflow_status,
2829
get_workflow_status_msg,
2930
)
3031

31-
3232
STOP_TIME = TimePoint(year=2006).to_local_time_zone()
3333

3434

@@ -79,29 +79,29 @@ def schd(
7979
'stopping: waiting for active jobs to complete'
8080
),
8181
(
82-
{'hold_point': 'point'},
82+
{'hold_point': 2},
8383
WorkflowStatus.RUNNING,
84-
WORKFLOW_STATUS_RUNNING_TO_HOLD % 'point'
84+
WORKFLOW_STATUS_RUNNING_TO_HOLD % 2
8585
),
8686
(
87-
{'stop_point': 'point'},
87+
{'stop_point': 4},
8888
WorkflowStatus.RUNNING,
89-
WORKFLOW_STATUS_RUNNING_TO_STOP % 'point'
89+
WORKFLOW_STATUS_RUNNING_TO_STOP % 4
9090
),
9191
(
9292
{'stop_clock_time': int(STOP_TIME.seconds_since_unix_epoch)},
9393
WorkflowStatus.RUNNING,
9494
WORKFLOW_STATUS_RUNNING_TO_STOP % str(STOP_TIME)
9595
),
9696
(
97-
{'stop_task_id': 'foo'},
97+
{'stop_task_id': '6/foo'},
9898
WorkflowStatus.RUNNING,
99-
WORKFLOW_STATUS_RUNNING_TO_STOP % 'foo'
99+
WORKFLOW_STATUS_RUNNING_TO_STOP % '6/foo'
100100
),
101101
(
102-
{'final_point': 'point'},
102+
{'final_point': 8},
103103
WorkflowStatus.RUNNING,
104-
WORKFLOW_STATUS_RUNNING_TO_STOP % 'point'
104+
WORKFLOW_STATUS_RUNNING_TO_STOP % 8
105105
),
106106
(
107107
{'is_stalled': True},
@@ -128,11 +128,48 @@ def schd(
128128
(
129129
{'is_stalled': True, 'is_paused': True},
130130
WorkflowStatus.PAUSED,
131-
'stalled (paused)'
131+
'stalled and paused',
132+
),
133+
(
134+
# earliest of stop point, hold point and stop task id
135+
{
136+
'stop_point': IntegerPoint(4),
137+
'hold_point': IntegerPoint(2),
138+
'stop_task_id': '6/foo',
139+
},
140+
WorkflowStatus.RUNNING,
141+
WORKFLOW_STATUS_RUNNING_TO_HOLD % 2,
142+
),
143+
(
144+
{
145+
'stop_point': IntegerPoint(11),
146+
'hold_point': IntegerPoint(15),
147+
'stop_task_id': '9/bar',
148+
},
149+
WorkflowStatus.RUNNING,
150+
WORKFLOW_STATUS_RUNNING_TO_STOP % '9/bar',
151+
),
152+
(
153+
{
154+
'stop_point': IntegerPoint(3),
155+
'hold_point': IntegerPoint(3),
156+
},
157+
WorkflowStatus.RUNNING,
158+
WORKFLOW_STATUS_RUNNING_TO_STOP % 3,
159+
),
160+
(
161+
# stop point trumps final point
162+
{
163+
'stop_point': IntegerPoint(1),
164+
'final_point': IntegerPoint(2),
165+
},
166+
WorkflowStatus.RUNNING,
167+
WORKFLOW_STATUS_RUNNING_TO_STOP % 1,
132168
),
133169
]
134170
)
135-
def test_get_workflow_status(kwargs, state, message):
171+
def test_get_workflow_status(kwargs, state, message, set_cycling_type):
172+
set_cycling_type()
136173
scheduler = schd(**kwargs)
137174
assert get_workflow_status(scheduler) == state
138175
assert get_workflow_status_msg(scheduler) == message

0 commit comments

Comments
 (0)