Skip to content

Commit 9d56622

Browse files
committed
Fix bug where a stalled paused workflow would have a status of running
1 parent ba57e88 commit 9d56622

File tree

5 files changed

+56
-68
lines changed

5 files changed

+56
-68
lines changed

changes.d/6200.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed bug where a stalled paused workflow would be incorrectly reported as running, not paused

cylc/flow/data_store_mgr.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@
8585
pdeepcopy,
8686
poverride
8787
)
88-
from cylc.flow.workflow_status import get_workflow_status
88+
from cylc.flow.workflow_status import (
89+
get_workflow_status,
90+
get_workflow_status_msg,
91+
)
8992
from cylc.flow.task_job_logs import JOB_LOG_OPTS, get_task_job_log
9093
from cylc.flow.task_proxy import TaskProxy
9194
from cylc.flow.task_state import (
@@ -2174,8 +2177,8 @@ def update_workflow(self, reloaded=False):
21742177
w_delta.latest_state_tasks[state].task_proxies[:] = tp_queue
21752178

21762179
# Set status & msg if changed.
2177-
status, status_msg = map(
2178-
str, get_workflow_status(self.schd))
2180+
status = get_workflow_status(self.schd).value
2181+
status_msg = get_workflow_status_msg(self.schd)
21792182
if w_data.status != status or w_data.status_msg != status_msg:
21802183
w_delta.status = status
21812184
w_delta.status_msg = status_msg

cylc/flow/scheduler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,7 @@ def update_data_store(self):
20002000
20012001
Call this method whenever the Scheduler's state has changed in a way
20022002
that requires a data store update.
2003-
See cylc.flow.workflow_status.get_workflow_status() for a
2003+
See cylc.flow.workflow_status.get_workflow_status_msg() for a
20042004
(non-exhaustive?) list of properties that if changed will require
20052005
this update.
20062006

cylc/flow/workflow_status.py

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"""Workflow status constants."""
1717

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

2121
from cylc.flow.wallclock import get_time_string_from_unix_time as time2str
2222

@@ -143,62 +143,41 @@ class AutoRestartMode(Enum):
143143
"""Workflow will stop immediately but *not* attempt to restart."""
144144

145145

146-
def get_workflow_status(schd: 'Scheduler') -> Tuple[str, str]:
147-
"""Return the status of the provided workflow.
148-
149-
This should be a short, concise description of the workflow state.
150-
151-
Args:
152-
schd: The running workflow
153-
154-
Returns:
155-
tuple - (state, state_msg)
156-
157-
state:
158-
The WorkflowState.
159-
state_msg:
160-
Text describing the current state (may be an empty string).
146+
def get_workflow_status(schd: 'Scheduler') -> WorkflowStatus:
147+
"""Return the status of the provided workflow."""
148+
if schd.stop_mode is not None:
149+
return WorkflowStatus.STOPPING
150+
if schd.is_paused or schd.reload_pending:
151+
return WorkflowStatus.PAUSED
152+
return WorkflowStatus.RUNNING
161153

162-
"""
163-
status = WorkflowStatus.RUNNING
164-
status_msg = ''
165154

155+
def get_workflow_status_msg(schd: 'Scheduler') -> str:
156+
"""Return a short, concise status message for the provided workflow."""
166157
if schd.stop_mode is not None:
167-
status = WorkflowStatus.STOPPING
168-
status_msg = f'stopping: {schd.stop_mode.explain()}'
169-
elif schd.reload_pending:
170-
status = WorkflowStatus.PAUSED
171-
status_msg = f'reloading: {schd.reload_pending}'
172-
elif schd.is_stalled:
173-
status_msg = 'stalled'
174-
elif schd.is_paused:
175-
status = WorkflowStatus.PAUSED
176-
status_msg = 'paused'
177-
elif schd.pool.hold_point:
178-
status_msg = (
179-
WORKFLOW_STATUS_RUNNING_TO_HOLD %
180-
schd.pool.hold_point)
181-
elif schd.pool.stop_point:
182-
status_msg = (
183-
WORKFLOW_STATUS_RUNNING_TO_STOP %
184-
schd.pool.stop_point)
185-
elif schd.stop_clock_time is not None:
186-
status_msg = (
187-
WORKFLOW_STATUS_RUNNING_TO_STOP %
188-
time2str(schd.stop_clock_time))
189-
elif schd.pool.stop_task_id:
190-
status_msg = (
191-
WORKFLOW_STATUS_RUNNING_TO_STOP %
192-
schd.pool.stop_task_id)
193-
elif schd.config and schd.config.final_point:
194-
status_msg = (
195-
WORKFLOW_STATUS_RUNNING_TO_STOP %
196-
schd.config.final_point)
197-
else:
198-
# fallback - running indefinitely
199-
status_msg = 'running'
200-
201-
return (status.value, status_msg)
158+
return f'stopping: {schd.stop_mode.explain()}'
159+
if schd.reload_pending:
160+
return f'reloading: {schd.reload_pending}'
161+
if schd.is_stalled:
162+
if schd.is_paused:
163+
return 'stalled (paused)'
164+
return 'stalled'
165+
if schd.is_paused:
166+
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
171+
if schd.stop_clock_time is not None:
172+
return WORKFLOW_STATUS_RUNNING_TO_STOP % time2str(
173+
schd.stop_clock_time
174+
)
175+
if schd.pool.stop_task_id:
176+
return WORKFLOW_STATUS_RUNNING_TO_STOP % schd.pool.stop_task_id
177+
if schd.config and schd.config.final_point:
178+
return WORKFLOW_STATUS_RUNNING_TO_STOP % schd.config.final_point
179+
# fallback - running indefinitely
180+
return 'running'
202181

203182

204183
class RunMode:

tests/unit/test_workflow_status.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@
1717
from types import SimpleNamespace
1818

1919
import pytest
20+
from metomi.isodatetime.data import TimePoint
2021

2122
from cylc.flow.workflow_status import (
2223
StopMode,
2324
WorkflowStatus,
2425
WORKFLOW_STATUS_RUNNING_TO_HOLD,
2526
WORKFLOW_STATUS_RUNNING_TO_STOP,
2627
get_workflow_status,
28+
get_workflow_status_msg,
2729
)
2830

2931

32+
STOP_TIME = TimePoint(year=2006).to_local_time_zone()
33+
34+
3035
def schd(
3136
final_point=None,
3237
hold_point=None,
@@ -50,6 +55,7 @@ def schd(
5055
stop_task_id=stop_task_id,
5156
),
5257
config=SimpleNamespace(final_point=final_point),
58+
options=SimpleNamespace(utc_mode=True),
5359
)
5460

5561

@@ -83,9 +89,9 @@ def schd(
8389
WORKFLOW_STATUS_RUNNING_TO_STOP % 'point'
8490
),
8591
(
86-
{'stop_clock_time': 1234},
92+
{'stop_clock_time': int(STOP_TIME.seconds_since_unix_epoch)},
8793
WorkflowStatus.RUNNING,
88-
WORKFLOW_STATUS_RUNNING_TO_STOP % ''
94+
WORKFLOW_STATUS_RUNNING_TO_STOP % str(STOP_TIME)
8995
),
9096
(
9197
{'stop_task_id': 'foo'},
@@ -112,22 +118,21 @@ def schd(
112118
(
113119
# stopping should trump stalled, paused & running
114120
{
115-
'stop_mode': StopMode.AUTO,
121+
'stop_mode': StopMode.REQUEST_NOW,
116122
'is_stalled': True,
117123
'is_paused': True
118124
},
119125
WorkflowStatus.STOPPING,
120-
'stopping'
126+
'stopping: shutting down'
121127
),
122128
(
123-
# stalled should trump paused & running
124129
{'is_stalled': True, 'is_paused': True},
125-
WorkflowStatus.RUNNING,
126-
'stalled'
130+
WorkflowStatus.PAUSED,
131+
'stalled (paused)'
127132
),
128133
]
129134
)
130135
def test_get_workflow_status(kwargs, state, message):
131-
state_, message_ = get_workflow_status(schd(**kwargs))
132-
assert state_ == state.value
133-
assert message in message_
136+
scheduler = schd(**kwargs)
137+
assert get_workflow_status(scheduler) == state
138+
assert get_workflow_status_msg(scheduler) == message

0 commit comments

Comments
 (0)