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