Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes.d/6554.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cylc show now displays that a task has been set to skip mode
8 changes: 8 additions & 0 deletions cylc/flow/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
is_relative_to,
)
from cylc.flow.task_qualifiers import ALT_QUALIFIERS
from cylc.flow.run_modes import WORKFLOW_ONLY_MODES
from cylc.flow.run_modes.simulation import configure_sim_mode
from cylc.flow.run_modes.skip import skip_mode_validate
from cylc.flow.subprocctx import SubFuncContext
Expand Down Expand Up @@ -2448,6 +2449,13 @@ def _get_taskdef(self, name: str) -> TaskDef:

try:
rtcfg = self.cfg['runtime'][name]

# If the workflow mode is simulation or dummy always
# override the task config:
workflow_run_mode = RunMode.get(self.options)
if workflow_run_mode.value in WORKFLOW_ONLY_MODES:
rtcfg['run mode'] = workflow_run_mode.value

except KeyError:
raise WorkflowConfigError("Task not defined: %s" % name) from None
# We may want to put in some handling for cases of changing the
Expand Down
4 changes: 2 additions & 2 deletions cylc/flow/network/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
)
from cylc.flow.id import Tokens
from cylc.flow.run_modes import (
TASK_CONFIG_RUN_MODES, WORKFLOW_RUN_MODES, RunMode)
WORKFLOW_RUN_MODES, RunMode)
from cylc.flow.task_outputs import SORT_ORDERS
from cylc.flow.task_state import (
TASK_STATUS_DESC,
Expand Down Expand Up @@ -633,7 +633,7 @@ class Meta:
# The run mode for the task.
TaskRunMode = graphene.Enum(
'TaskRunMode',
[(m.capitalize(), m) for m in TASK_CONFIG_RUN_MODES],
[(k.capitalize(), k.lower()) for k in RunMode.__members__.keys()],
description=lambda x: RunMode(x.value).describe() if x else None,
)

Expand Down
8 changes: 8 additions & 0 deletions cylc/flow/run_modes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ def get(options: 'Values') -> "RunMode":
return RunMode(run_mode)
return RunMode.LIVE

@classmethod
def _missing_(cls, value):
value = value.lower()
for member in cls:
if member.value.lower() == value:
return member
return None

def get_submit_method(self) -> 'Optional[SubmissionInterface]':
"""Return the job submission method for this run mode.

Expand Down
8 changes: 8 additions & 0 deletions cylc/flow/scripts/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from cylc.flow.id import Tokens
from cylc.flow.id_cli import parse_ids
from cylc.flow.network.client_factory import get_client
from cylc.flow.run_modes import RunMode
from cylc.flow.task_outputs import TaskOutputs
from cylc.flow.task_state import (
TASK_STATUSES_ORDERED,
Expand Down Expand Up @@ -145,6 +146,7 @@
}
runtime {
completion
runMode
}
}
}
Expand Down Expand Up @@ -346,9 +348,15 @@ async def prereqs_and_outputs_query(
attrs.append("queued")
if t_proxy['isRunahead']:
attrs.append("runahead")
if (
t_proxy['runtime']['runMode']
and RunMode(t_proxy['runtime']['runMode']) != RunMode.LIVE
):
attrs.append(f"run mode={t_proxy['runtime']['runMode']}")
state_msg = state
if attrs:
state_msg += f" ({','.join(attrs)})"

ansiprint(f'<bold>state:</bold> {state_msg}')

# flow numbers, if not just 1
Expand Down
5 changes: 4 additions & 1 deletion tests/flakyfunctional/cylc-show/00-simple.t
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ cmp_json "${TEST_NAME}-taskinstance" "${TEST_NAME}-taskinstance" \
}
}
},
"runtime": {"completion": "(started and succeeded)"},
"runtime": {
"completion": "(started and succeeded)",
"runMode": "Live"
},
"prerequisites": [
{
"expression": "0",
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/run_modes/test_nonlive.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def test_db_task_jobs(
'R1': '&'.join(KGO)}},
'runtime': {
mode: {'run mode': mode} for mode in KGO}
}))
}), run_mode='live')
async with start(schd):
# Reference all task proxies so we can examine them
# at the end of the test:
Expand Down
85 changes: 75 additions & 10 deletions tests/integration/scripts/test_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import json
import pytest
import re
from types import SimpleNamespace

from colorama import init as colour_init
Expand All @@ -26,6 +27,9 @@
)


RE_STATE = re.compile('state:.*')


@pytest.fixture(scope='module')
def mod_my_conf():
"""A workflow configuration with some workflow metadata."""
Expand Down Expand Up @@ -59,8 +63,8 @@ def mod_my_conf():
'destroyedtheworldyet.com/'
),
'question': 'mutually exclusive',
}
}
},
},
},
}

Expand Down Expand Up @@ -128,6 +132,7 @@ async def test_task_meta_query(mod_my_schd, capsys):
)
assert ret == 0
out, err = capsys.readouterr()

assert out.splitlines() == [
'title: Task Title',
'question: mutually exclusive',
Expand Down Expand Up @@ -170,9 +175,9 @@ async def test_task_instance_query(
'scheduling': {
'graph': {'R1': 'zed & dog & cat & ant'},
},
}
},
),
paused_start=False
paused_start=False,
)
async with start(schd):
await schd.update_data_structure()
Expand All @@ -195,20 +200,32 @@ async def test_task_instance_query(
]


@pytest.mark.parametrize(
'workflow_run_mode, run_mode_info',
(
('live', 'Skip'),
('dummy', 'Dummy'),
('simulation', 'Simulation'),
)
)
@pytest.mark.parametrize(
'attributes_bool, flow_nums, expected_state, expected_flows',
[
pytest.param(
False, [1], 'state: waiting', None,
False, [1], 'state: waiting (run mode={})', None,
),
pytest.param(
True, [1, 2], 'state: waiting (held,queued,runahead)', 'flows: [1,2]',
True,
[1, 2],
'state: waiting (held,queued,runahead,run mode={})',
'flows: [1,2]',
)
]
)
async def test_task_instance_state_flows(
flow, scheduler, start, capsys,
attributes_bool, flow_nums, expected_state, expected_flows
workflow_run_mode, run_mode_info,
attributes_bool, flow_nums, expected_state, expected_flows
):
"""It should print task instance state, attributes, and flows."""

Expand All @@ -225,9 +242,13 @@ async def test_task_instance_state_flows(
'scheduling': {
'graph': {'R1': 'a'},
},
}
'runtime': {
'a': {'run mode': 'skip'}
}
},
),
paused_start=True
paused_start=True,
run_mode=workflow_run_mode,
)
async with start(schd):

Expand Down Expand Up @@ -257,7 +278,7 @@ async def test_task_instance_state_flows(
line for line in out.splitlines()
if line.startswith("state:")
] == [
expected_state,
expected_state.format(run_mode_info),
]
if expected_flows is not None:
assert [
Expand All @@ -266,3 +287,47 @@ async def test_task_instance_state_flows(
] == [
expected_flows,
]


async def test_task_run_mode_changes(flow, scheduler, start, capsys):
"""Broadcasting a change of run mode changes run mode shown by cylc show.
"""
opts = SimpleNamespace(
comms_timeout=5,
json=False,
task_defs=None,
list_prereqs=False,
)
schd = scheduler(
flow({'scheduling': {'graph': {'R1': 'a'}}}),
run_mode='live'
)

async with start(schd):
# Control: No mode set, the Run Mode setting is not shown:
await schd.update_data_structure()
ret = await show(
schd.workflow,
[Tokens('//1/a')],
opts,
)
assert ret == 0
out, _ = capsys.readouterr()
state, = RE_STATE.findall(out)
assert 'waiting' in state

# Broadcast change task to skip mode:
schd.broadcast_mgr.put_broadcast(['1'], ['a'], [{'run mode': 'skip'}])
await schd.update_data_structure()

# show now shows skip mode:
ret = await show(
schd.workflow,
[Tokens('//1/a')],
opts,
)
assert ret == 0

out, _ = capsys.readouterr()
state, = RE_STATE.findall(out)
assert 'run mode=Skip' in state
2 changes: 1 addition & 1 deletion tests/integration/tui/screenshots/test_show.success.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">description: The first metasyntactic </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">variable. </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">URL: (not given) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">state: waiting (queued) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">state: waiting (queued,run mode=Simulation) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">prerequisites: (None) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">outputs: (&#x27;⨯&#x27;: not completed) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> ⨯ 1/foo expired </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">│</span><span style="color:#000000;background:#e5e5e5"> </span>
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/run_modes/test_run_modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"""Tests for utilities supporting run modes.
"""

import pytest

from cylc.flow.run_modes import RunMode


Expand All @@ -28,3 +30,9 @@ def test_run_mode_desc():
def test_get_default_live():
"""RunMode.get() => live"""
assert RunMode.get({}) == RunMode.LIVE


@pytest.mark.parametrize('str_', ('LIVE', 'Dummy', 'SkIp', 'siMuLATioN'))
def test__missing_(str_):
"""The RunMode enumeration works when fed a string in the wrong case"""
assert RunMode(str_).value == str_.lower()