Skip to content

Commit 1e59531

Browse files
committed
Response to reviews
Update changes.d/6583.fix.md Co-authored-by: Hilary James Oliver <[email protected]> Update cylc/flow/config.py Co-authored-by: Hilary James Oliver <[email protected]> respond to Hilary's review: Don't check all outputs only terminals. use regexs to sort out issues with qualifiers, suicide triggers &c mypy
1 parent 035afd7 commit 1e59531

File tree

4 files changed

+51
-28
lines changed

4 files changed

+51
-28
lines changed

changes.d/6583.fix.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Fix bug where graph items with undefined outputs were missed at validation if the graph item was not an upstream dependency of another graph item.
1+
Fix bug where undefined outputs were missed by validation if no tasks trigger off of them.

cylc/flow/config.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
get_trigger_completion_variable_maps,
100100
trigger_to_completion_variable,
101101
)
102+
from cylc.flow.task_qualifiers import TASK_QUALIFIERS
102103
from cylc.flow.run_modes import RunMode
103104
from cylc.flow.task_trigger import TaskTrigger, Dependency
104105
from cylc.flow.taskdef import TaskDef
@@ -2266,17 +2267,10 @@ def load_graph(self):
22662267
self.workflow_polling_tasks.update(
22672268
parser.workflow_state_polling_tasks)
22682269
self._proc_triggers(parser, seq, task_triggers)
2269-
self.check_outputs(
2270-
[
2271-
task_output
2272-
for task_output in parser.task_output_opt
2273-
if task_output[0]
2274-
in [
2275-
task_output.split(':')[0]
2276-
for task_output in parser.terminals
2277-
]
2278-
]
2279-
)
2270+
2271+
# Checking for undefined outputs for terminal tasks. Tasks with
2272+
# dependencies are checked in generate_triggers:
2273+
self.check_outputs(parser.terminals)
22802274

22812275
self.set_required_outputs(task_output_opt)
22822276

@@ -2290,22 +2284,19 @@ def load_graph(self):
22902284
for tdef in self.taskdefs.values():
22912285
tdef.tweak_outputs()
22922286

2293-
def check_outputs(
2294-
self, tasks_and_outputs: Iterable[Tuple[str, str]]
2295-
) -> None:
2287+
def check_outputs(self, terminals: Iterable[str]) -> None:
22962288
"""Check that task outputs have been registered with tasks.
22972289
2298-
Args: tasks_and_outputs: ((task, output), ...)
2299-
2300-
Raises: WorkflowConfigError is a user has defined a task with a
2301-
custom output, but has not registered a custom output.
2290+
Raises: WorkflowConfigError if a custom output is not defined.
23022291
"""
2303-
for task, output in tasks_and_outputs:
2304-
registered_outputs = self.cfg['runtime'][task]['outputs']
2305-
if (
2306-
not TaskOutputs.is_valid_std_name(output)
2307-
and output not in registered_outputs
2308-
):
2292+
terminal_outputs = [
2293+
(a[0].strip("!"), b)
2294+
for a in (t.split(':') for t in terminals if ":" in t)
2295+
if (b := a[1].strip("?")) not in TASK_QUALIFIERS
2296+
]
2297+
2298+
for task, output in terminal_outputs:
2299+
if output not in self.cfg['runtime'][task]['outputs']:
23092300
raise WorkflowConfigError(
23102301
f"Undefined custom output: {task}:{output}"
23112302
)

tests/integration/reftests/test_pre_initial.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ async def test_drop(flow, scheduler, reftest):
9494
}
9595

9696

97-
async def test_over_bracketed(flow, scheduler, reftest):
97+
async def test_over_bracketed(flow, scheduler, reftest, validate):
9898
"""Test nested conditional simplification for pre-initial cycling."""
9999
wid = flow({
100100
'scheduling': {
@@ -108,6 +108,7 @@ async def test_over_bracketed(flow, scheduler, reftest):
108108
},
109109
},
110110
})
111+
validate(wid)
111112
schd = scheduler(wid, paused_start=False)
112113

113114
assert await reftest(schd) == {

tests/unit/test_config.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

1717
import os
18-
import sys
1918
from optparse import Values
2019
from typing import (
2120
TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type)
2221
import pytest
2322
import logging
23+
from textwrap import dedent
2424
from types import SimpleNamespace
2525
from contextlib import suppress
2626

@@ -47,6 +47,10 @@
4747

4848
from cylc.flow.cycling.iso8601 import ISO8601Point
4949

50+
51+
param = pytest.param
52+
53+
5054
if TYPE_CHECKING:
5155
from pathlib import Path
5256
Fixture = Any
@@ -1703,7 +1707,6 @@ def test_cylc_env_at_parsing(
17031707

17041708
def test_force_workflow_compat_mode(tmp_path):
17051709
fpath = (tmp_path / 'flow.cylc')
1706-
from textwrap import dedent
17071710
fpath.write_text(dedent("""
17081711
[scheduler]
17091712
allow implicit tasks = true
@@ -1716,3 +1719,31 @@ def test_force_workflow_compat_mode(tmp_path):
17161719
WorkflowConfig('foo', str(fpath), {})
17171720
# It succeeds with compat mode:
17181721
WorkflowConfig('foo', str(fpath), {}, force_compat_mode=True)
1722+
1723+
1724+
@pytest.mark.parametrize(
1725+
'registered_outputs, tasks_and_outputs, fails',
1726+
(
1727+
param([], ['foo:x'], True, id='output-unregistered'),
1728+
param([], ['foo:x?'], True, id='optional-output-unregistered'),
1729+
param([], ['foo'], False, id='no-modifier-unregistered'),
1730+
param(['x'], ['foo:x'], False, id='output-registered'),
1731+
param([], ['foo:succeed'], False, id='alt-default-ok'),
1732+
param([], ['foo:failed'], False, id='default-ok'),
1733+
)
1734+
)
1735+
def test_check_outputs(tmp_path, registered_outputs, tasks_and_outputs, fails):
1736+
(tmp_path / 'flow.cylc').write_text(dedent("""
1737+
[scheduler]
1738+
allow implicit tasks = true
1739+
[scheduling]
1740+
[[graph]]
1741+
R1 = foo
1742+
"""))
1743+
cfg = WorkflowConfig('', tmp_path / 'flow.cylc', '')
1744+
cfg.cfg['runtime']['foo']['outputs'] = registered_outputs
1745+
if fails:
1746+
with pytest.raises(WorkflowConfigError, match='Undefined custom output'):
1747+
cfg.check_outputs(tasks_and_outputs)
1748+
else:
1749+
assert cfg.check_outputs(tasks_and_outputs) is None

0 commit comments

Comments
 (0)