From 147020af4bab6f5d76a86337fead24d292c4df8b Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Mon, 13 Jan 2025 15:33:33 +0000
Subject: [PATCH 01/14] add run mode skip to cylc show
---
changes.d/6554.feat.md | 1 +
cylc/flow/scripts/show.py | 7 +++++++
tests/integration/scripts/test_show.py | 15 +++++++++++----
3 files changed, 19 insertions(+), 4 deletions(-)
create mode 100644 changes.d/6554.feat.md
diff --git a/changes.d/6554.feat.md b/changes.d/6554.feat.md
new file mode 100644
index 00000000000..61a78c2deea
--- /dev/null
+++ b/changes.d/6554.feat.md
@@ -0,0 +1 @@
+Cylc show now displays that a task has been set to skip mode
diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py
index c387a3f3fcf..248fb632558 100755
--- a/cylc/flow/scripts/show.py
+++ b/cylc/flow/scripts/show.py
@@ -145,6 +145,7 @@
}
runtime {
completion
+ runMode
}
}
}
@@ -346,9 +347,15 @@ async def prereqs_and_outputs_query(
attrs.append("queued")
if t_proxy['isRunahead']:
attrs.append("runahead")
+ if (
+ t_proxy['runtime']['runMode']
+ and t_proxy['runtime']['runMode'] != 'Live'
+ ):
+ attrs.append(f"run mode={t_proxy['runtime']['runMode']}")
state_msg = state
if attrs:
state_msg += f" ({','.join(attrs)})"
+
ansiprint(f'state: {state_msg}')
# flow numbers, if not just 1
diff --git a/tests/integration/scripts/test_show.py b/tests/integration/scripts/test_show.py
index ec000a75a16..9d01e65245a 100644
--- a/tests/integration/scripts/test_show.py
+++ b/tests/integration/scripts/test_show.py
@@ -59,8 +59,8 @@ def mod_my_conf():
'destroyedtheworldyet.com/'
),
'question': 'mutually exclusive',
- }
- }
+ },
+ },
},
}
@@ -128,6 +128,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',
@@ -199,10 +200,13 @@ async def test_task_instance_query(
'attributes_bool, flow_nums, expected_state, expected_flows',
[
pytest.param(
- False, [1], 'state: waiting', None,
+ False, [1], 'state: waiting (skip)', None,
),
pytest.param(
- True, [1, 2], 'state: waiting (held,queued,runahead)', 'flows: [1,2]',
+ True,
+ [1, 2],
+ 'state: waiting (held,queued,runahead,skip)',
+ 'flows: [1,2]',
)
]
)
@@ -225,6 +229,9 @@ async def test_task_instance_state_flows(
'scheduling': {
'graph': {'R1': 'a'},
},
+ 'runtime': {
+ 'a': {'run mode': 'skip'}
+ }
}
),
paused_start=True
From 818efc0cfcf7b6243b58744b232c26f312320f0a Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Tue, 14 Jan 2025 11:20:33 +0000
Subject: [PATCH 02/14] test changing task mode with broadcast
broaden the definition of task-mode
Save workflow states to the runtime config object.
---
cylc/flow/config.py | 8 +++
cylc/flow/network/schema.py | 5 +-
tests/integration/scripts/test_show.py | 75 +++++++++++++++++++++++---
3 files changed, 78 insertions(+), 10 deletions(-)
diff --git a/cylc/flow/config.py b/cylc/flow/config.py
index c438cd9105a..c7568d8d801 100644
--- a/cylc/flow/config.py
+++ b/cylc/flow/config.py
@@ -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
@@ -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
diff --git a/cylc/flow/network/schema.py b/cylc/flow/network/schema.py
index 67d3c58ec84..0ca742452fa 100644
--- a/cylc/flow/network/schema.py
+++ b/cylc/flow/network/schema.py
@@ -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,
@@ -633,7 +633,8 @@ 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()],
+ # [(m.capitalize(), m) for m in TASK_CONFIG_RUN_MODES],
description=lambda x: RunMode(x.value).describe() if x else None,
)
diff --git a/tests/integration/scripts/test_show.py b/tests/integration/scripts/test_show.py
index 9d01e65245a..3ea1b83ecee 100644
--- a/tests/integration/scripts/test_show.py
+++ b/tests/integration/scripts/test_show.py
@@ -16,6 +16,7 @@
import json
import pytest
+import re
from types import SimpleNamespace
from colorama import init as colour_init
@@ -26,6 +27,9 @@
)
+RE_STATE = re.compile('state:.*')
+
+
@pytest.fixture(scope='module')
def mod_my_conf():
"""A workflow configuration with some workflow metadata."""
@@ -171,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()
@@ -196,23 +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 (skip)', None,
+ False, [1], 'state: waiting (run mode={})', None,
),
pytest.param(
True,
[1, 2],
- 'state: waiting (held,queued,runahead,skip)',
+ '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."""
@@ -232,9 +245,10 @@ async def test_task_instance_state_flows(
'runtime': {
'a': {'run mode': 'skip'}
}
- }
+ },
),
- paused_start=True
+ paused_start=True,
+ run_mode=workflow_run_mode,
)
async with start(schd):
@@ -264,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 [
@@ -273,3 +287,48 @@ async def test_task_instance_state_flows(
] == [
expected_flows,
]
+
+
+async def test_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'}}}),
+ paused_start=True,
+ 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
From 7c626a391498928cf6fe8ebb2bffce8eb41503fc Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Thu, 16 Jan 2025 10:20:31 +0000
Subject: [PATCH 03/14] fixed a broken test
fixed TUI test
fix a broken functional test
---
tests/flakyfunctional/cylc-show/00-simple.t | 5 ++++-
tests/integration/run_modes/test_nonlive.py | 2 +-
tests/integration/tui/screenshots/test_show.success.html | 2 +-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/tests/flakyfunctional/cylc-show/00-simple.t b/tests/flakyfunctional/cylc-show/00-simple.t
index d0a700dd865..471515de6b0 100644
--- a/tests/flakyfunctional/cylc-show/00-simple.t
+++ b/tests/flakyfunctional/cylc-show/00-simple.t
@@ -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",
diff --git a/tests/integration/run_modes/test_nonlive.py b/tests/integration/run_modes/test_nonlive.py
index e62d250ce99..214a801f216 100644
--- a/tests/integration/run_modes/test_nonlive.py
+++ b/tests/integration/run_modes/test_nonlive.py
@@ -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:
diff --git a/tests/integration/tui/screenshots/test_show.success.html b/tests/integration/tui/screenshots/test_show.success.html
index 7f1caebe49d..d3e2e30c8af 100644
--- a/tests/integration/tui/screenshots/test_show.success.html
+++ b/tests/integration/tui/screenshots/test_show.success.html
@@ -13,7 +13,7 @@
│ description: The first metasyntactic │
│ variable. │
│ URL: (not given) │
- │ state: waiting (queued) │
+ │ state: waiting (queued,run mode=Simulation) │
│ prerequisites: (None) │
│ outputs: ('⨯': not completed) │
│ ⨯ 1/foo expired │
From 4ca26a0f6d0bea5b7bedd3403c3ec093f53edb08 Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Fri, 17 Jan 2025 08:11:18 +0000
Subject: [PATCH 04/14] Apply suggestions from code review
Co-authored-by: Oliver Sanders
---
cylc/flow/scripts/show.py | 3 ++-
tests/integration/scripts/test_show.py | 3 +--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py
index 248fb632558..01fa20da2d0 100755
--- a/cylc/flow/scripts/show.py
+++ b/cylc/flow/scripts/show.py
@@ -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,
@@ -349,7 +350,7 @@ async def prereqs_and_outputs_query(
attrs.append("runahead")
if (
t_proxy['runtime']['runMode']
- and t_proxy['runtime']['runMode'] != 'Live'
+ and t_proxy['runtime']['runMode'] != RunMode.LIVE.value
):
attrs.append(f"run mode={t_proxy['runtime']['runMode']}")
state_msg = state
diff --git a/tests/integration/scripts/test_show.py b/tests/integration/scripts/test_show.py
index 3ea1b83ecee..7c9befbacc6 100644
--- a/tests/integration/scripts/test_show.py
+++ b/tests/integration/scripts/test_show.py
@@ -289,7 +289,7 @@ async def test_task_instance_state_flows(
]
-async def test_mode_changes(flow, scheduler, start, capsys):
+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(
@@ -300,7 +300,6 @@ async def test_mode_changes(flow, scheduler, start, capsys):
)
schd = scheduler(
flow({'scheduling': {'graph': {'R1': 'a'}}}),
- paused_start=True,
run_mode='live'
)
From c397b61ac100950a8bf1d28b7e1255eb425ab377 Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Thu, 23 Jan 2025 08:52:07 +0000
Subject: [PATCH 05/14] fix broken test
---
cylc/flow/network/schema.py | 1 -
cylc/flow/scripts/show.py | 3 ++-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cylc/flow/network/schema.py b/cylc/flow/network/schema.py
index 0ca742452fa..0ab26c33b19 100644
--- a/cylc/flow/network/schema.py
+++ b/cylc/flow/network/schema.py
@@ -634,7 +634,6 @@ class Meta:
TaskRunMode = graphene.Enum(
'TaskRunMode',
[(k.capitalize(), k.lower()) for k in RunMode.__members__.keys()],
- # [(m.capitalize(), m) for m in TASK_CONFIG_RUN_MODES],
description=lambda x: RunMode(x.value).describe() if x else None,
)
diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py
index 01fa20da2d0..0b0c735f8e0 100755
--- a/cylc/flow/scripts/show.py
+++ b/cylc/flow/scripts/show.py
@@ -350,7 +350,8 @@ async def prereqs_and_outputs_query(
attrs.append("runahead")
if (
t_proxy['runtime']['runMode']
- and t_proxy['runtime']['runMode'] != RunMode.LIVE.value
+ and t_proxy['runtime']['runMode'].lower()
+ != RunMode.LIVE.value
):
attrs.append(f"run mode={t_proxy['runtime']['runMode']}")
state_msg = state
From c651f7099a221d4b2c5a0250f02b194a12994a0b Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Thu, 23 Jan 2025 09:00:15 +0000
Subject: [PATCH 06/14] make run mode enum extend string to allow easier
comparison, and allow it to digest strings in any state of capitalization
---
cylc/flow/run_modes/__init__.py | 10 +++++++++-
cylc/flow/scripts/show.py | 3 +--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/cylc/flow/run_modes/__init__.py b/cylc/flow/run_modes/__init__.py
index 2f094631380..a0037daee79 100644
--- a/cylc/flow/run_modes/__init__.py
+++ b/cylc/flow/run_modes/__init__.py
@@ -40,7 +40,7 @@
]
-class RunMode(Enum):
+class RunMode(str, Enum):
"""The possible run modes of a task/workflow."""
LIVE = 'live'
@@ -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.lower() == value:
+ return member
+ return None
+
def get_submit_method(self) -> 'Optional[SubmissionInterface]':
"""Return the job submission method for this run mode.
diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py
index 0b0c735f8e0..0f353c75a80 100755
--- a/cylc/flow/scripts/show.py
+++ b/cylc/flow/scripts/show.py
@@ -350,8 +350,7 @@ async def prereqs_and_outputs_query(
attrs.append("runahead")
if (
t_proxy['runtime']['runMode']
- and t_proxy['runtime']['runMode'].lower()
- != RunMode.LIVE.value
+ and RunMode(t_proxy['runtime']['runMode']) != RunMode.LIVE
):
attrs.append(f"run mode={t_proxy['runtime']['runMode']}")
state_msg = state
From 1bbecbbf90f1b7f005d7ec1f79a32be49cd3e8fd Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Thu, 23 Jan 2025 14:33:55 +0000
Subject: [PATCH 07/14] remove run-mode inheriting str
---
cylc/flow/run_modes/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cylc/flow/run_modes/__init__.py b/cylc/flow/run_modes/__init__.py
index a0037daee79..ff7425bd6e0 100644
--- a/cylc/flow/run_modes/__init__.py
+++ b/cylc/flow/run_modes/__init__.py
@@ -40,7 +40,7 @@
]
-class RunMode(str, Enum):
+class RunMode(Enum):
"""The possible run modes of a task/workflow."""
LIVE = 'live'
From 37aa3e4c0c33c245652325d0a736b16a28604369 Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Thu, 23 Jan 2025 14:38:36 +0000
Subject: [PATCH 08/14] added a test for RunMode._missing_
---
cylc/flow/run_modes/__init__.py | 2 +-
tests/unit/run_modes/test_run_modes.py | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/cylc/flow/run_modes/__init__.py b/cylc/flow/run_modes/__init__.py
index ff7425bd6e0..70f0d42ea83 100644
--- a/cylc/flow/run_modes/__init__.py
+++ b/cylc/flow/run_modes/__init__.py
@@ -89,7 +89,7 @@ def get(options: 'Values') -> "RunMode":
def _missing_(cls, value):
value = value.lower()
for member in cls:
- if member.lower() == value:
+ if member.value.lower() == value:
return member
return None
diff --git a/tests/unit/run_modes/test_run_modes.py b/tests/unit/run_modes/test_run_modes.py
index 57d245016d1..6cdd3b01672 100644
--- a/tests/unit/run_modes/test_run_modes.py
+++ b/tests/unit/run_modes/test_run_modes.py
@@ -16,6 +16,8 @@
"""Tests for utilities supporting run modes.
"""
+import pytest
+
from cylc.flow.run_modes import RunMode
@@ -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()
From d2b701dd59afbd8c37ce552b4e75989940eee1ab Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Fri, 24 Jan 2025 09:15:48 +0000
Subject: [PATCH 09/14] Update cylc/flow/scripts/show.py
Co-authored-by: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
---
cylc/flow/scripts/show.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/cylc/flow/scripts/show.py b/cylc/flow/scripts/show.py
index 0f353c75a80..bb1665a2b8d 100755
--- a/cylc/flow/scripts/show.py
+++ b/cylc/flow/scripts/show.py
@@ -348,11 +348,9 @@ 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']}")
+ run_mode = t_proxy['runtime']['runMode']
+ if run_mode and RunMode(run_mode) != RunMode.LIVE:
+ attrs.append(f"run mode={run_mode}")
state_msg = state
if attrs:
state_msg += f" ({','.join(attrs)})"
From a72e4b0b9399f9fabe5982f880391a68ebd54130 Mon Sep 17 00:00:00 2001
From: Tim Pillinger <26465611+wxtim@users.noreply.github.com>
Date: Fri, 24 Jan 2025 09:16:02 +0000
Subject: [PATCH 10/14] Update changes.d/6554.feat.md
Co-authored-by: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
---
changes.d/6554.feat.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/changes.d/6554.feat.md b/changes.d/6554.feat.md
index 61a78c2deea..1abebb4e369 100644
--- a/changes.d/6554.feat.md
+++ b/changes.d/6554.feat.md
@@ -1 +1 @@
-Cylc show now displays that a task has been set to skip mode
+`cylc show` now displays when a task has been set to skip mode
From e0e4133ce9455b53064e6162eb044a75959d36f3 Mon Sep 17 00:00:00 2001
From: Tim Pillinger
Date: Mon, 10 Mar 2025 09:46:28 +0000
Subject: [PATCH 11/14] use option_parsers to set up test.
---
tests/integration/scripts/test_show.py | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/tests/integration/scripts/test_show.py b/tests/integration/scripts/test_show.py
index d523ab1db55..426ba82e24d 100644
--- a/tests/integration/scripts/test_show.py
+++ b/tests/integration/scripts/test_show.py
@@ -22,7 +22,9 @@
from colorama import init as colour_init
from cylc.flow.id import Tokens
+from cylc.flow.option_parsers import Options
from cylc.flow.scripts.show import (
+ get_option_parser,
show,
)
@@ -293,12 +295,7 @@ async def test_task_instance_state_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,
- )
+ opts = Options(get_option_parser())()
schd = scheduler(
flow({'scheduling': {'graph': {'R1': 'a'}}}),
run_mode='live'
From eb855c045498adcc65a12fbb802fb6eba6367793 Mon Sep 17 00:00:00 2001
From: Tim Pillinger
Date: Mon, 10 Mar 2025 10:41:18 +0000
Subject: [PATCH 12/14] Prevent broadcast manager doing anything with run mode
= if workflow run mode is simulation or dummy.
---
cylc/flow/broadcast_mgr.py | 18 +++++++++++++++++-
cylc/flow/scheduler.py | 2 +-
tests/integration/run_modes/test_simulation.py | 15 +++++++++++++--
3 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/cylc/flow/broadcast_mgr.py b/cylc/flow/broadcast_mgr.py
index b9329114302..3738b730098 100644
--- a/cylc/flow/broadcast_mgr.py
+++ b/cylc/flow/broadcast_mgr.py
@@ -32,6 +32,8 @@
from cylc.flow.exceptions import PointParsingError
from cylc.flow.parsec.util import listjoin, pdeepcopy, poverride
from cylc.flow.parsec.validate import BroadcastConfigValidator
+from cylc.flow.run_modes import WORKFLOW_ONLY_MODES
+
if TYPE_CHECKING:
from cylc.flow.id import Tokens
@@ -62,7 +64,8 @@ class BroadcastMgr:
REC_SECTION = re.compile(r"\[([^\]]+)\]")
- def __init__(self, workflow_db_mgr, data_store_mgr):
+ def __init__(self, workflow_db_mgr, data_store_mgr, run_mode):
+ self.workflow_run_mode = run_mode
self.workflow_db_mgr = workflow_db_mgr
self.data_store_mgr = data_store_mgr
self.linearized_ancestors = {}
@@ -289,6 +292,19 @@ def put_broadcast(
SPEC['runtime']['__MANY__'],
)
+ # Skip and warn if a run mode is broadcast to a workflow
+ # running in simulation or dummy mode.
+ if (
+ 'run mode' in coerced_setting
+ and self.workflow_run_mode.value in WORKFLOW_ONLY_MODES
+ ):
+ LOG.warning(
+ f'Broadcasting {coerced_setting} to a workflow'
+ f' running in {self.workflow_run_mode.value} mode'
+ ' will have no effect, and will not be actioned.'
+ )
+ continue
+
for point_string in point_strings or []:
# Standardise the point and check its validity.
bad_point = False
diff --git a/cylc/flow/scheduler.py b/cylc/flow/scheduler.py
index 888280b2681..a1364bd6e47 100644
--- a/cylc/flow/scheduler.py
+++ b/cylc/flow/scheduler.py
@@ -403,7 +403,7 @@ async def initialise(self):
"""
self.data_store_mgr = DataStoreMgr(self)
self.broadcast_mgr = BroadcastMgr(
- self.workflow_db_mgr, self.data_store_mgr)
+ self.workflow_db_mgr, self.data_store_mgr, self.get_run_mode())
self.server = WorkflowRuntimeServer(self)
diff --git a/tests/integration/run_modes/test_simulation.py b/tests/integration/run_modes/test_simulation.py
index de8a3f53031..aee9f1585d0 100644
--- a/tests/integration/run_modes/test_simulation.py
+++ b/tests/integration/run_modes/test_simulation.py
@@ -16,6 +16,7 @@
"""Test the workings of simulation mode"""
+import logging
from pathlib import Path
import pytest
from pytest import param
@@ -371,7 +372,7 @@ async def test_settings_reload(
async def test_settings_broadcast(
- flow, scheduler, start, monkeytime
+ flow, scheduler, start, monkeytime, log_filter
):
"""Assert that broadcasting a change in the settings for a task
affects subsequent psuedo-submissions.
@@ -415,12 +416,22 @@ async def test_settings_broadcast(
# Change a setting using broadcast:
schd.broadcast_mgr.put_broadcast(
['1066'], ['one'], [{
- 'simulation': {'fail cycle points': ''}
+ 'simulation': {'fail cycle points': ''},
}])
# Submit again - result is different:
schd.task_job_mgr.submit_nonlive_task_jobs([itask], RunMode.SIMULATION)
assert itask.mode_settings.sim_task_fails is False
+ # Assert that setting run mode on a simulation mode task fails with
+ # warning:
+ schd.broadcast_mgr.put_broadcast(
+ ['1066'], ['one'], [{
+ 'run mode': 'live',
+ }])
+ record = log_filter(contains='will not be actioned')[0]
+ assert record[0] == logging.WARNING
+ assert 'run mode' not in schd.broadcast_mgr.broadcasts
+
# Assert Clearing the broadcast works
schd.broadcast_mgr.clear_broadcast()
schd.task_job_mgr.submit_nonlive_task_jobs([itask], RunMode.SIMULATION)
From d645d45569c753e7933f8f4c7278749bfb4b3327 Mon Sep 17 00:00:00 2001
From: Tim Pillinger
Date: Mon, 10 Mar 2025 11:24:08 +0000
Subject: [PATCH 13/14] trivial cov increase
---
tests/unit/run_modes/test_run_modes.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tests/unit/run_modes/test_run_modes.py b/tests/unit/run_modes/test_run_modes.py
index 6cdd3b01672..cf4587fc161 100644
--- a/tests/unit/run_modes/test_run_modes.py
+++ b/tests/unit/run_modes/test_run_modes.py
@@ -36,3 +36,12 @@ def test_get_default_live():
def test__missing_(str_):
"""The RunMode enumeration works when fed a string in the wrong case"""
assert RunMode(str_).value == str_.lower()
+
+
+def test__missing_still_doesnt_work():
+ """
+ The RunMode enumeration raises an error when fed a string
+ that is not a mode.
+ """
+ with pytest.raises(ValueError):
+ RunMode('garbage')
From 06e5b941e57a68e2a0cc6a5aed35d2ce5ca786f6 Mon Sep 17 00:00:00 2001
From: Tim Pillinger
Date: Mon, 10 Mar 2025 13:41:46 +0000
Subject: [PATCH 14/14] response to reveiw
---
tests/integration/scripts/test_show.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/tests/integration/scripts/test_show.py b/tests/integration/scripts/test_show.py
index 426ba82e24d..e4490164752 100644
--- a/tests/integration/scripts/test_show.py
+++ b/tests/integration/scripts/test_show.py
@@ -22,9 +22,8 @@
from colorama import init as colour_init
from cylc.flow.id import Tokens
-from cylc.flow.option_parsers import Options
from cylc.flow.scripts.show import (
- get_option_parser,
+ ShowOptions,
show,
)
@@ -295,7 +294,7 @@ async def test_task_instance_state_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 = Options(get_option_parser())()
+ opts = ShowOptions()
schd = scheduler(
flow({'scheduling': {'graph': {'R1': 'a'}}}),
run_mode='live'