60
60
from cylc .flow .id_cli import contains_fnmatch
61
61
from cylc .flow .id_match import filter_ids
62
62
from cylc .flow .platforms import get_platform
63
+ from cylc .flow .prerequisite import PrereqTuple
63
64
from cylc .flow .run_modes import RunMode
64
65
from cylc .flow .run_modes .skip import process_outputs as get_skip_mode_outputs
65
66
from cylc .flow .task_action_timer import (
@@ -1867,30 +1868,23 @@ def _get_task_proxy_db_outputs(
1867
1868
self ._load_historical_outputs (itask )
1868
1869
return itask
1869
1870
1870
- def _standardise_prereqs (
1871
- self , prereqs : 'List[str]'
1872
- ) -> 'Dict[Tokens, str]' :
1873
- """Convert prerequisites to a map of task messages: outputs.
1874
-
1875
- (So satsify_me logs failures)
1876
-
1877
- """
1878
- _prereqs = {}
1871
+ def _standardise_prereqs (self , prereqs : 'List[str]' ) -> 'Set[PrereqTuple]' :
1872
+ """Convert trigger prerequisites to task messages."""
1873
+ _prereqs = set ()
1879
1874
for prereq in prereqs :
1880
1875
pre = Tokens (prereq , relative = True )
1881
- # add implicit "succeeded"; convert "succeed" to "succeeded" etc.
1876
+ # Convert "succeed" to "succeeded" etc.
1882
1877
output = TaskTrigger .standardise_name (
1883
1878
pre ['task_sel' ] or TASK_OUTPUT_SUCCEEDED )
1884
1879
# Convert outputs to task messages.
1885
1880
try :
1886
1881
msg = self .config .get_taskdef (
1887
- pre ['task' ]
1882
+ str ( pre ['task' ])
1888
1883
).outputs [output ][0 ]
1889
1884
cycle = standardise_point_string (pre ['cycle' ])
1890
1885
except KeyError :
1891
- # The task does not have this output.
1892
1886
LOG .warning (
1893
- f"output { pre .relative_id_with_selectors } not found" )
1887
+ f"Output { pre .relative_id_with_selectors } not found" )
1894
1888
continue
1895
1889
except WorkflowConfigError as exc :
1896
1890
LOG .warning (
@@ -1899,7 +1893,7 @@ def _standardise_prereqs(
1899
1893
LOG .warning (
1900
1894
f'Invalid prerequisite cycle point:\n { exc .args [0 ]} ' )
1901
1895
else :
1902
- _prereqs [ pre . duplicate ( task_sel = msg , cycle = cycle )] = prereq
1896
+ _prereqs . add ( PrereqTuple ( str ( cycle ), str ( pre [ "task" ]), msg ))
1903
1897
return _prereqs
1904
1898
1905
1899
def _standardise_outputs (
@@ -1914,11 +1908,26 @@ def _standardise_outputs(
1914
1908
msg = tdef .outputs [output ][0 ]
1915
1909
except KeyError :
1916
1910
LOG .warning (
1917
- f"output { point } /{ tdef .name } :{ output } not found" )
1911
+ f"Output { point } /{ tdef .name } :{ output } not found" )
1918
1912
continue
1919
1913
_outputs .append (msg )
1920
1914
return _outputs
1921
1915
1916
+ def _get_prereq_params (
1917
+ self , prereqs : 'List[str]' , tdef : 'TaskDef' , point : 'PointBase'
1918
+ ) -> Tuple [bool , 'Iterable[Tokens]' ]:
1919
+ """Convert input prerequisites to Tokens of just the valid ones.
1920
+
1921
+ And convert the "['all']" prerequisite shortcut to a bool.
1922
+ """
1923
+ if prereqs != ['all' ]:
1924
+ set_all = False
1925
+ valid_prereqs = self ._get_valid_prereqs (prereqs , tdef , point )
1926
+ else :
1927
+ set_all = True
1928
+ valid_prereqs = []
1929
+ return set_all , valid_prereqs
1930
+
1922
1931
def set_prereqs_and_outputs (
1923
1932
self ,
1924
1933
items : Iterable [str ],
@@ -1936,17 +1945,17 @@ def set_prereqs_and_outputs(
1936
1945
- spawn the task (if not spawned)
1937
1946
- update its prerequisites
1938
1947
1948
+ Prerequisite format: "cycle/task:output" or "all".
1949
+
1950
+ Prerequisite validity is checked via the taskdef prior to spawning
1951
+ so we can easily back out it if no valid prerequisites are given.
1952
+
1939
1953
Set outputs:
1940
1954
- update task outputs in the DB
1941
1955
- (implied outputs are handled by the event manager)
1942
1956
- spawn children of the outputs (if not spawned)
1943
1957
- update the child prerequisites
1944
1958
1945
- Task matching restrictions (for now):
1946
- - globs (cycle and name) only match in the pool
1947
- - inactive tasks must be specified individually
1948
- - family names are not expanded to members
1949
-
1950
1959
Uses a transient task proxy to spawn children. (Even if parent was
1951
1960
previously spawned in this flow its children might not have been).
1952
1961
@@ -1958,20 +1967,21 @@ def set_prereqs_and_outputs(
1958
1967
1959
1968
Args:
1960
1969
items: task ID match patterns
1961
- prereqs: prerequisites to set
1970
+ prereqs: prerequisites to set ([pre1, pre2,...], ['all'] or [])
1962
1971
outputs: outputs to set
1963
1972
flow: flow numbers for spawned or merged tasks
1964
1973
flow_wait: wait for flows to catch up before continuing
1965
1974
flow_descr: description of new flow
1966
1975
1967
1976
"""
1968
1977
# Get matching pool tasks and inactive task definitions.
1969
- itasks , inactive_tasks , unmatched = self .filter_task_proxies (
1978
+ itasks , inactive_tasks , _ = self .filter_task_proxies (
1970
1979
items ,
1971
1980
inactive = True ,
1972
1981
warn_no_active = False ,
1973
1982
)
1974
1983
1984
+ no_op = True
1975
1985
flow_nums = self ._get_flow_nums (flow , flow_descr )
1976
1986
1977
1987
# Set existing task proxies.
@@ -1982,34 +1992,87 @@ def set_prereqs_and_outputs(
1982
1992
f" { repr_flow_nums (itask .flow_nums , full = True )} "
1983
1993
)
1984
1994
continue
1985
- self . merge_flows ( itask , flow_nums )
1995
+
1986
1996
if prereqs :
1987
- self ._set_prereqs_itask (itask , prereqs , flow_nums )
1997
+ set_all , valid_prereqs = (
1998
+ self ._get_prereq_params (prereqs , itask .tdef , itask .point )
1999
+ )
2000
+ if not (set_all or valid_prereqs ):
2001
+ continue
2002
+ self .merge_flows (itask , flow_nums )
2003
+ self ._set_prereqs_itask (itask , valid_prereqs , set_all )
2004
+ no_op = False
1988
2005
else :
2006
+ # Outputs (may be empty list)
1989
2007
# Spawn as if seq xtrig of parentless task was satisfied,
1990
2008
# with associated task producing these outputs.
2009
+ self .merge_flows (itask , flow_nums )
1991
2010
self .check_spawn_psx_task (itask )
1992
2011
self ._set_outputs_itask (itask , outputs )
2012
+ no_op = False
1993
2013
1994
- # Spawn and set inactive tasks.
1995
2014
if not flow :
1996
2015
# default: assign to all active flows
1997
2016
flow_nums = self ._get_active_flow_nums ()
2017
+
2018
+ # Spawn and set inactive tasks.
1998
2019
for tdef , point in inactive_tasks :
1999
2020
if prereqs :
2021
+ set_all , valid_prereqs = (
2022
+ self ._get_prereq_params (prereqs , tdef , point )
2023
+ )
2024
+ if not (set_all or valid_prereqs ):
2025
+ continue
2000
2026
self ._set_prereqs_tdef (
2001
- point , tdef , prereqs , flow_nums , flow_wait )
2027
+ point , tdef , valid_prereqs , flow_nums , flow_wait , set_all )
2028
+ no_op = False
2002
2029
else :
2030
+ # Outputs (may be empty list)
2003
2031
trans = self ._get_task_proxy_db_outputs (
2004
2032
point , tdef , flow_nums ,
2005
2033
flow_wait = flow_wait , transient = True
2006
2034
)
2007
2035
if trans is not None :
2008
2036
self ._set_outputs_itask (trans , outputs )
2037
+ no_op = False
2009
2038
2010
- if self .compute_runahead ():
2039
+ if not no_op and self .compute_runahead ():
2011
2040
self .release_runahead_tasks ()
2012
2041
2042
+ def _get_valid_prereqs (
2043
+ self , prereqs : List [str ], tdef : 'TaskDef' , point : 'PointBase'
2044
+ ) -> 'Iterable[Tokens]' :
2045
+ """Validate prerequisite triggers and return associated task messages.
2046
+
2047
+ To set prerequisites, the user gives triggers, but we need to use the
2048
+ associated task messages to satisfy the prerequisites of target tasks.
2049
+
2050
+ Args:
2051
+ prereqs:
2052
+ list of string prerequisites of the form "point/task:output"
2053
+ Returns:
2054
+ set of tokens {(cycle, task, task_message),}
2055
+
2056
+ """
2057
+ valid = {key for pre in tdef .get_prereqs (point ) for key in pre .keys ()}
2058
+
2059
+ # Get prerequisite tuples in terms of task messages not triggers.
2060
+ requested = self ._standardise_prereqs (prereqs )
2061
+
2062
+ for prereq in requested - valid :
2063
+ # But log bad ones with triggers, not messages.
2064
+ trg = self .config .get_taskdef (
2065
+ prereq .task
2066
+ ).get_output (prereq .output )
2067
+ LOG .warning (
2068
+ f'{ point } /{ tdef .name } does not depend on '
2069
+ f'"{ prereq .get_id ()} :{ trg } "'
2070
+ )
2071
+ return {
2072
+ Tokens (cycle = pre .point , task = pre .task , task_sel = pre .output )
2073
+ for pre in valid & requested
2074
+ }
2075
+
2013
2076
def _set_outputs_itask (
2014
2077
self ,
2015
2078
itask : 'TaskProxy' ,
@@ -2020,8 +2083,11 @@ def _set_outputs_itask(
2020
2083
If no outputs were specified and the task has no required outputs to
2021
2084
set, set the "success pathway" outputs in the same way that skip mode
2022
2085
does.
2086
+
2087
+ Designated flows should already be merged to the task proxy.
2023
2088
"""
2024
2089
outputs = set (outputs )
2090
+
2025
2091
if not outputs :
2026
2092
outputs = set (
2027
2093
# Set required outputs by default
@@ -2066,51 +2132,42 @@ def _set_outputs_itask(
2066
2132
def _set_prereqs_itask (
2067
2133
self ,
2068
2134
itask : 'TaskProxy' ,
2069
- prereqs : 'List[str ]' ,
2070
- flow_nums : 'Set[int]' ,
2071
- ) -> bool :
2135
+ prereqs : 'Iterable[Tokens ]' ,
2136
+ set_all : bool
2137
+ ) -> None :
2072
2138
"""Set prerequisites on a task proxy.
2073
2139
2074
- Prerequisite format: "cycle/task:output" or "all".
2075
-
2076
- Return True if any prereqs are valid, else False.
2077
-
2140
+ Designated flows should already be merged to the task proxy.
2078
2141
"""
2079
- if prereqs == [ "all" ] :
2142
+ if set_all :
2080
2143
itask .state .set_prerequisites_all_satisfied ()
2081
2144
else :
2082
- # Attempt to set the given presrequisites.
2083
- # Log any that aren't valid for the task.
2084
- presus = self ._standardise_prereqs (prereqs )
2085
- unmatched = itask .satisfy_me (presus .keys (), forced = True )
2086
- for task_msg in unmatched :
2087
- LOG .warning (
2088
- f"{ itask .identity } does not depend on"
2089
- f' "{ presus [task_msg ]} "'
2090
- )
2091
- if len (unmatched ) == len (prereqs ):
2092
- # No prereqs matched.
2093
- return False
2145
+ itask .satisfy_me (prereqs , forced = True )
2094
2146
if (
2095
2147
self .runahead_limit_point is not None
2096
2148
and itask .point <= self .runahead_limit_point
2097
2149
):
2098
2150
self .rh_release_and_queue (itask )
2099
2151
self .data_store_mgr .delta_task_prerequisite (itask )
2100
- return True
2101
2152
2102
2153
def _set_prereqs_tdef (
2103
- self , point , taskdef , prereqs , flow_nums , flow_wait
2154
+ self ,
2155
+ point : 'PointBase' ,
2156
+ taskdef : 'TaskDef' ,
2157
+ prereqs : 'Iterable[Tokens]' ,
2158
+ flow_nums : 'FlowNums' ,
2159
+ flow_wait : bool ,
2160
+ set_all : bool
2104
2161
):
2105
2162
"""Spawn an inactive task and set prerequisites on it."""
2106
-
2107
2163
itask = self .spawn_task (
2108
2164
taskdef .name , point , flow_nums , flow_wait = flow_wait
2109
2165
)
2110
2166
if itask is None :
2111
2167
return
2112
- if self ._set_prereqs_itask (itask , prereqs , flow_nums ):
2113
- self .add_to_pool (itask )
2168
+
2169
+ self ._set_prereqs_itask (itask , prereqs , set_all )
2170
+ self .add_to_pool (itask )
2114
2171
2115
2172
def _get_active_flow_nums (self ) -> 'FlowNums' :
2116
2173
"""Return all active flow numbers.
@@ -2396,7 +2453,14 @@ def filter_task_proxies(
2396
2453
warn_no_active : bool = True ,
2397
2454
inactive : bool = False ,
2398
2455
) -> 'Tuple[List[TaskProxy], Set[Tuple[TaskDef, PointBase]], List[str]]' :
2399
- """Return task proxies that match names, points, states in items.
2456
+ """Return task proxies and inactive tasks that match ids.
2457
+
2458
+ (TODO: method should be renamed to "filter_tasks").
2459
+
2460
+ Restrictions (for now):
2461
+ - globs (cycle and name) only match in the pool
2462
+ - inactive tasks must be specified individually
2463
+ - family names are not expanded to members
2400
2464
2401
2465
Args:
2402
2466
ids:
0 commit comments