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