37
37
from cylc .flow .task_proxy import TaskProxy
38
38
39
39
40
+ # Exotic: Recursive Type hint.
41
+ NestedDict = Dict [str , Union ['NestedDict' , Any ]]
42
+
43
+
40
44
def configure_sim_modes (taskdefs , sim_mode ):
41
45
"""Adjust task defs for simulation and dummy mode.
42
46
@@ -45,51 +49,71 @@ def configure_sim_modes(taskdefs, sim_mode):
45
49
46
50
for tdef in taskdefs :
47
51
# Compute simulated run time by scaling the execution limit.
48
- rtc = tdef .rtconfig
49
- sleep_sec = get_simulated_run_len (rtc )
52
+ configure_sim_modes_rtc (tdef .rtconfig , dummy_mode )
50
53
51
- rtc ['execution time limit' ] = (
52
- sleep_sec + DurationParser ().parse (str (
53
- rtc ['simulation' ]['time limit buffer' ])).get_seconds ()
54
- )
55
54
56
- rtc ['simulation' ]['simulated run length' ] = sleep_sec
57
- rtc ['submission retry delays' ] = [1 ]
55
+ def configure_sim_modes_rtc (rtc , dummy_mode ):
56
+ sleep_sec = get_simulated_run_len (rtc )
57
+
58
+ rtc ['execution time limit' ] = (
59
+ sleep_sec + DurationParser ().parse (str (
60
+ rtc ['simulation' ]['time limit buffer' ])).get_seconds ()
61
+ )
62
+
63
+ rtc ['simulation' ]['simulated run length' ] = sleep_sec
64
+ rtc ['submission retry delays' ] = [1 ]
58
65
66
+ if dummy_mode :
59
67
# Generate dummy scripting.
60
68
rtc ['init-script' ] = ""
61
69
rtc ['env-script' ] = ""
62
70
rtc ['pre-script' ] = ""
63
71
rtc ['post-script' ] = ""
64
72
rtc ['script' ] = build_dummy_script (
65
73
rtc , sleep_sec ) if dummy_mode else ""
74
+ else :
75
+ rtc ['script' ] = ""
76
+
77
+ disable_platforms (rtc )
66
78
67
- disable_platforms ( rtc )
79
+ rtc [ 'platform' ] = 'localhost'
68
80
69
- # Disable environment, in case it depends on env-script.
70
- rtc ['environment' ] = {}
81
+ # Disable environment, in case it depends on env-script.
82
+ rtc ['environment' ] = {}
71
83
72
- rtc ["simulation" ][
73
- "fail cycle points"
74
- ] = parse_fail_cycle_points (
75
- rtc ["simulation" ]["fail cycle points" ]
76
- )
84
+ rtc ["simulation" ][
85
+ "fail cycle points"
86
+ ] = parse_fail_cycle_points (
87
+ rtc ["simulation" ]["fail cycle points" ]
88
+ )
77
89
78
90
79
91
def get_simulated_run_len (rtc : Dict [str , Any ]) -> int :
80
92
"""Get simulated run time.
81
93
82
- rtc = run time config
94
+ Args:
95
+ rtc: run time config
96
+
97
+ Returns:
98
+ Number of seconds to sleep for in sim mode.
83
99
"""
100
+ # Simulated run length acts as a flag that this is at runtime:
101
+ # If durations have already been parsed, trying to parse them
102
+ # again will result in failures.
103
+ recalc = bool (rtc ['simulation' ].get ('simulated run length' , '' ))
84
104
limit = rtc ['execution time limit' ]
85
105
speedup = rtc ['simulation' ]['speedup factor' ]
86
- if limit and speedup :
106
+
107
+ if limit and speedup and recalc :
108
+ sleep_sec = limit / speedup
109
+ elif limit and speedup :
87
110
sleep_sec = (DurationParser ().parse (
88
111
str (limit )).get_seconds () / speedup )
112
+ elif recalc :
113
+ sleep_sec = rtc ['simulation' ]['default run length' ]
89
114
else :
90
- sleep_sec = DurationParser ().parse (
91
- str (rtc ['simulation' ]['default run length' ])
92
- ).get_seconds ()
115
+ default_run_len = str (rtc ['simulation' ]['default run length' ])
116
+ sleep_sec = DurationParser ().parse (default_run_len ).get_seconds ()
93
117
94
118
return sleep_sec
95
119
@@ -147,7 +171,7 @@ def parse_fail_cycle_points(
147
171
[]
148
172
"""
149
173
f_pts : 'Optional[List[PointBase]]'
150
- if 'all' in f_pts_orig :
174
+ if f_pts_orig is None or 'all' in f_pts_orig :
151
175
f_pts = None
152
176
else :
153
177
f_pts = []
@@ -156,8 +180,64 @@ def parse_fail_cycle_points(
156
180
return f_pts
157
181
158
182
183
+ def unpack_dict (dict_ : NestedDict , parent_key : str = '' ) -> Dict [str , Any ]:
184
+ """Unpack a nested dict into a single layer.
185
+
186
+ Examples:
187
+ >>> unpack_dict({'foo': 1, 'bar': {'baz': 2, 'qux':3}})
188
+ {'foo': 1, 'bar.baz': 2, 'bar.qux': 3}
189
+ >>> unpack_dict({'foo': {'example': 42}, 'bar': {"1":2, "3":4}})
190
+ {'foo.example': 42, 'bar.1': 2, 'bar.3': 4}
191
+
192
+ """
193
+ output = {}
194
+ for key , value in dict_ .items ():
195
+ new_key = parent_key + '.' + key if parent_key else key
196
+ if isinstance (value , dict ):
197
+ output .update (unpack_dict (value , new_key ))
198
+ else :
199
+ output [new_key ] = value
200
+
201
+ return output
202
+
203
+
204
+ def nested_dict_path_update (
205
+ dict_ : NestedDict , path : List [Any ], value : Any
206
+ ) -> NestedDict :
207
+ """Set a value in a nested dict.
208
+
209
+ Examples:
210
+ >>> nested_dict_path_update({'foo': {'bar': 1}}, ['foo', 'bar'], 42)
211
+ {'foo': {'bar': 42}}
212
+ """
213
+ this = dict_
214
+ for i in range (len (path )):
215
+ if isinstance (this [path [i ]], dict ):
216
+ this = this [path [i ]]
217
+ else :
218
+ this [path [i ]] = value
219
+ return dict_
220
+
221
+
222
+ def update_nested_dict (rtc : NestedDict , dict_ : NestedDict ) -> None :
223
+ """Update one config nested dictionary with the contents of another.
224
+
225
+ Examples:
226
+ >>> x = {'foo': {'bar': 12}, 'qux': 77}
227
+ >>> y = {'foo': {'bar': 42}}
228
+ >>> update_nested_dict(x, y)
229
+ >>> print(x)
230
+ {'foo': {'bar': 42}, 'qux': 77}
231
+ """
232
+ for keylist , value in unpack_dict (dict_ ).items ():
233
+ keys = keylist .split ('.' )
234
+ rtc = nested_dict_path_update (rtc , keys , value )
235
+
236
+
159
237
def sim_time_check (
160
- message_queue : 'Queue[TaskMsg]' , itasks : 'List[TaskProxy]'
238
+ message_queue : 'Queue[TaskMsg]' ,
239
+ itasks : 'List[TaskProxy]' ,
240
+ broadcast_mgr : Optional [Any ] = None
161
241
) -> bool :
162
242
"""Check if sim tasks have been "running" for as long as required.
163
243
@@ -166,9 +246,16 @@ def sim_time_check(
166
246
Returns:
167
247
True if _any_ simulated task state has changed.
168
248
"""
249
+
169
250
sim_task_state_changed = False
170
251
now = time ()
171
252
for itask in itasks :
253
+ if broadcast_mgr :
254
+ broadcast = broadcast_mgr .get_broadcast (itask .tokens )
255
+ if broadcast :
256
+ update_nested_dict (
257
+ itask .tdef .rtconfig , broadcast )
258
+ configure_sim_modes_rtc (itask .tdef .rtconfig , False )
172
259
if itask .state .status != TASK_STATUS_RUNNING :
173
260
continue
174
261
# Started time is not set on restart
0 commit comments