@@ -251,6 +251,7 @@ class GraphParser:
251
251
rf"""
252
252
(!)? # suicide mark
253
253
({ _RE_NODE } ) # node name
254
+ ({ _RE_OFFSET } )? # cycle point offset
254
255
({ _RE_QUAL } )? # trigger qualifier
255
256
({ _RE_OPT } )? # optional output indicator
256
257
""" ,
@@ -469,10 +470,9 @@ def parse_graph(self, graph_string: str) -> None:
469
470
pairs .add ((chain [i ], chain [i + 1 ]))
470
471
471
472
# Get a set of RH nodes which are not at the LH of another pair:
472
- pairs_dict = dict (pairs )
473
- terminals = set (pairs_dict .values ()).difference (pairs_dict .keys ())
473
+ terminals = {p [1 ] for p in pairs }.difference ({p [0 ] for p in pairs })
474
474
475
- for pair in pairs :
475
+ for pair in sorted ( pairs , key = lambda p : str ( p [ 0 ])) :
476
476
self ._proc_dep_pair (pair , terminals )
477
477
478
478
@classmethod
@@ -540,16 +540,12 @@ def _proc_dep_pair(
540
540
raise GraphParseError (mismatch_msg .format (right ))
541
541
542
542
# Raise error for cycle point offsets at the end of chains
543
- if '[' in right :
544
- if left and (right in terminals ):
545
- # This right hand side is at the end of a chain:
546
- raise GraphParseError (
547
- 'Invalid cycle point offsets only on right hand '
548
- 'side of a dependency (must be on left hand side):'
549
- f' { left } => { right } ' )
550
- else :
551
- # This RHS is also a LHS in a chain:
552
- return
543
+ if '[' in right and left and (right in terminals ):
544
+ # This right hand side is at the end of a chain:
545
+ raise GraphParseError (
546
+ 'Invalid cycle point offsets only on right hand '
547
+ 'side of a dependency (must be on left hand side):'
548
+ f' { left } => { right } ' )
553
549
554
550
# Split right side on AND.
555
551
rights = right .split (self .__class__ .OP_AND )
@@ -887,15 +883,15 @@ def _compute_triggers(
887
883
raise ValueError ( # pragma: no cover
888
884
f"Unexpected graph expression: '{ right } '"
889
885
)
890
- suicide_char , name , output , opt_char = m .groups ()
886
+ suicide_char , name , offset , output , opt_char = m .groups ()
891
887
suicide = (suicide_char == self .__class__ .SUICIDE )
892
888
optional = (opt_char == self .__class__ .OPTIONAL )
893
889
if output :
894
890
output = output .strip (self .__class__ .QUALIFIER )
895
891
896
892
if name in self .family_map :
897
893
fam = True
898
- mems = self .family_map [name ]
894
+ rhs_members = self .family_map [name ]
899
895
if not output :
900
896
# (Plain family name on RHS).
901
897
# Make implicit success explicit.
@@ -922,10 +918,13 @@ def _compute_triggers(
922
918
else :
923
919
# Convert to standard output names if necessary.
924
920
output = TaskTrigger .standardise_name (output )
925
- mems = [name ]
921
+ rhs_members = [name ]
926
922
outputs = [output ]
927
923
928
- for mem in mems :
929
- self ._set_triggers (mem , suicide , trigs , expr , orig_expr )
924
+ for mem in rhs_members :
925
+ if not offset :
926
+ # Nodes with offsets on the RHS do not define triggers.
927
+ self ._set_triggers (mem , suicide , trigs , expr , orig_expr )
930
928
for output in outputs :
929
+ # But they must be consistent with output optionality.
931
930
self ._set_output_opt (mem , output , optional , suicide , fam )
0 commit comments