Skip to content

Commit fe710ef

Browse files
committed
refactor in response to review
1 parent 152ddb5 commit fe710ef

File tree

2 files changed

+28
-93
lines changed

2 files changed

+28
-93
lines changed

cylc/flow/graph_parser.py

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -471,37 +471,24 @@ def parse_graph(self, graph_string: str) -> None:
471471
pairs.add((chain[i], chain[i + 1]))
472472

473473
# Get a set of RH nodes which are not at the LH of another pair:
474-
self.terminals = self.get_graph_terminals(pairs)
474+
# terminals = {p[1] for p in pairs}.difference({p[0] for p in pairs})
475475

476-
for pair in sorted(pairs, key=lambda p: str(p[0])):
477-
self._proc_dep_pair(pair, self.terminals)
478-
479-
@staticmethod
480-
def get_graph_terminals(pairs):
481-
"""Get terminating ends of graphs.
476+
check_terminals = {}
477+
lefts = set()
478+
rights = set()
482479

483-
For example in `foo => bar => baz` only `baz` terminates the chain
484-
of dependencies.
480+
for pair in sorted(pairs, key=lambda p: str(p[0])):
481+
self._proc_dep_pair(pair, check_terminals, lefts, rights)
485482

486-
Examples:
487-
>>> this = GraphParser.get_graph_terminals
488-
>>> this({('foo', 'bar')})
489-
{'bar'}
490-
"""
491-
lefts = []
492-
rights = []
493-
for left, right in pairs:
494-
if left and ('&' in left or '|' in left):
495-
# RE used because don't want to have to worry about
496-
# mutiple and/or and cleaning whitespace.
497-
lefts += GraphParser._RE_ANDOR.split(left)
498-
else:
499-
lefts.append(left)
500-
if right and ('&' in right or '|' in right):
501-
rights += GraphParser._RE_ANDOR.split(right)
502-
else:
503-
rights.append(right)
504-
return set(rights).difference(set(lefts))
483+
self.terminals = rights.difference(lefts)
484+
for right in self.terminals:
485+
left = check_terminals.get(right)
486+
if left:
487+
raise GraphParseError(
488+
'Invalid cycle point offsets only on right hand'
489+
' side of dependency (must be on left hand side):'
490+
f'{left} => {right}'
491+
)
505492

506493
@classmethod
507494
def _report_invalid_lines(cls, lines: List[str]) -> None:
@@ -532,7 +519,9 @@ def _report_invalid_lines(cls, lines: List[str]) -> None:
532519
def _proc_dep_pair(
533520
self,
534521
pair: Tuple[Optional[str], str],
535-
terminals: Set[str],
522+
check_terminals: Dict[str, str],
523+
_lefts: Set[str],
524+
_rights: Set[str],
536525
) -> None:
537526
"""Process a single dependency pair 'left => right'.
538527
@@ -568,12 +557,8 @@ def _proc_dep_pair(
568557
raise GraphParseError(mismatch_msg.format(right))
569558

570559
# Raise error for cycle point offsets at the end of chains
571-
if '[' in right and left and (right in terminals):
572-
# This right hand side is at the end of a chain:
573-
raise GraphParseError(
574-
'Invalid cycle point offsets only on right hand '
575-
'side of a dependency (must be on left hand side):'
576-
f' {left} => {right}')
560+
if '[' in right and left:
561+
check_terminals[right] = left
577562

578563
# Split right side on AND.
579564
rights = right.split(self.__class__.OP_AND)
@@ -594,12 +579,15 @@ def _proc_dep_pair(
594579
raise GraphParseError(
595580
f"Null task name in graph: {left} => {right}")
596581

582+
_rights.update(*([rights] or []))
583+
597584
for left in lefts:
598585
# Extract information about all nodes on the left.
599586

600587
if left:
601588
info = self.__class__.REC_NODES.findall(left)
602589
expr = left
590+
_lefts.add(left)
603591

604592
else:
605593
# There is no left-hand-side task.

tests/unit/test_graph_parser.py

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -967,13 +967,13 @@ def test_RHS_AND(graph: str, expected_triggers: Dict[str, List[str]]):
967967
@pytest.mark.parametrize(
968968
'args, err',
969969
(
970-
# Error if offset in terminal RHS:
971-
param((('a', 'b[-P42M]'), {'b[-P42M]'}), 'Invalid cycle point offset'),
972970
# No error if offset in NON-terminal RHS:
973-
param((('a', 'b[-P42M]'), {}), None),
971+
param((('a', 'b[-P42M]'), {}, set(), set()), None),
974972
# Check the left hand side if this has a non-terminal RHS:
975-
param((('a &', 'b[-P42M]'), {}), 'Null task name in graph'),
976-
)
973+
param(
974+
(('a &', 'b[-P42M]'), {}, set(), set()), 'Null task name in graph'
975+
),
976+
),
977977
)
978978
def test_proc_dep_pair(args, err):
979979
"""
@@ -985,56 +985,3 @@ def test_proc_dep_pair(args, err):
985985
gp._proc_dep_pair(*args)
986986
else:
987987
assert gp._proc_dep_pair(*args) is None
988-
989-
990-
@pytest.mark.parametrize(
991-
'pairs, terminals',
992-
(
993-
param(
994-
{
995-
(None, 'foo[-P1D]:restart1'),
996-
(None, 'bar?'),
997-
('foo[-P1D]:restart1|bar?', 'foo')
998-
},
999-
{'foo'},
1000-
id='|'
1001-
),
1002-
param(
1003-
{
1004-
(None, 'foo[-P1D]:restart1'),
1005-
(None, 'bar?'),
1006-
('foo[-P1D]:restart1 & bar?', 'foo')
1007-
},
1008-
{'foo'},
1009-
id='&'
1010-
),
1011-
param(
1012-
{
1013-
(None, 'foo[-P1D]:restart1'),
1014-
(None, 'bar?'),
1015-
(None, 'qux'),
1016-
('foo[-P1D]:restart1 &bar? |qux', 'foo')
1017-
},
1018-
{'foo'},
1019-
id='&-and-|'
1020-
),
1021-
param(
1022-
{
1023-
(None, 'stop1'),
1024-
('stop1', 't3:start&t4:start'),
1025-
('t3:start&t4:start', 'stop2?')
1026-
},
1027-
{'stop2?'},
1028-
id='diamond-graph'
1029-
),
1030-
param(
1031-
{(None, 'stop1'), ('stop1', 't3:start&t4')},
1032-
{'t3:start', 't4'},
1033-
id='y-shape-graph'
1034-
),
1035-
)
1036-
)
1037-
def test_get_graph_terminals(pairs, terminals):
1038-
"""It identifies all graph terminals, and no non terminals.
1039-
"""
1040-
assert GraphParser.get_graph_terminals(pairs) == terminals

0 commit comments

Comments
 (0)