diff --git a/CHANGES.md b/CHANGES.md index 7cdbd2e6c77..0ded1be62bc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -47,6 +47,19 @@ creating a new release entry be sure to copy & paste the span tag with the `actions:bind` attribute, which is used by a regex to find the text to be updated. Only the first match gets replaced, so it's fine to leave the old ones in. --> +------------------------------------------------------------------------------- +## __cylc-8.0b3 (???)__ + +Fourth beta release of Cylc 8. + +(See note on cylc-8 backward-incompatible changes, above) + +### Enhancements + +[#4335](https://github.com/cylc/cylc-flow/pull/4335) - have validation catch +erroneous use of both `expr => bar` and `expr => !bar` in the same graph. + + ------------------------------------------------------------------------------- ## __cylc-8.0b2 (Released 2021-07-28)__ diff --git a/cylc/flow/graph_parser.py b/cylc/flow/graph_parser.py index 91f688c5a5b..9474e8629eb 100644 --- a/cylc/flow/graph_parser.py +++ b/cylc/flow/graph_parser.py @@ -16,6 +16,7 @@ """Module for parsing cylc graph strings.""" import re +import contextlib from cylc.flow.exceptions import GraphParseError from cylc.flow.param_expand import GraphExpander @@ -506,6 +507,18 @@ def _add_trigger(self, orig_expr, rights, expr, info): else: members = [right] for member in members: + with contextlib.suppress(KeyError): + osuicide = self.triggers[member][expr][1] + # This trigger already exists, so we must have both + # "expr => member" and "expr => !member" in the graph, + # or simply a duplicate trigger not recognized earlier + # because of parameter offsets. + if suicide or osuicide: + oexp = re.sub(r'(&|\|)', r' \1 ', orig_expr) + oexp = re.sub(r':succeed', '', oexp) + raise GraphParseError( + f"{oexp} can't trigger both {member} and !{member}" + ) self.triggers.setdefault(member, {}) self.original.setdefault(member, {}) self.triggers[member][expr] = (trigs, suicide) diff --git a/tests/unit/test_graph_parser.py b/tests/unit/test_graph_parser.py index 618852fcc97..153f3c1c210 100644 --- a/tests/unit/test_graph_parser.py +++ b/tests/unit/test_graph_parser.py @@ -44,6 +44,16 @@ def test_parse_graph_fails_with_spaces_in_task_name(self): with self.assertRaises(GraphParseError): self.parser.parse_graph("a b => c") + def test_parse_graph_fails_with_suicide_and_not_suicide(self): + """Test graph parser fails with both "expr => !foo" + and "expr => !foo" in the same graph.""" + with self.assertRaises(GraphParseError): + self.parser.parse_graph( + """(a | b & c) => d + foo => bar + (a | b & c) => !d + """) + def test_parse_graph_fails_with_invalid_and_operator(self): """Test that the graph parse will fail when the and operator is not correctly used."""