1313from multiprocessing import queues
1414
1515from . import pipelines , config
16- from .logging import logger , events , system_statistics , run_log , node_cost , slack
16+ from .logging import logger , pipeline_events , system_statistics , run_log , node_cost
17+ from . import events
1718
1819
1920def run_pipeline (pipeline : pipelines .Pipeline , nodes : {pipelines .Node } = None ,
20- with_upstreams : bool = False ) -> [events .Event ]:
21+ with_upstreams : bool = False ,
22+ interactively_started : bool = False
23+ ) -> [events .Event ]:
2124 """
2225 Runs a pipeline in a forked sub process. Acts as a generator that yields events from the sub process.
2326
@@ -129,17 +132,23 @@ def track_finished_pipelines():
129132 in dict (running_pipelines ).items (): # type: pipelines.Pipeline
130133 if len (set (running_pipeline .nodes .values ()) & processed_nodes ) == len (running_pipeline .nodes ):
131134 succeeded = running_pipeline not in failed_pipelines
132- event_queue .put (events .Output (
135+ event_queue .put (pipeline_events .Output (
133136 node_path = running_pipeline .path (), format = logger .Format .ITALICS , is_error = not succeeded ,
134137 message = f'{ "succeeded" if succeeded else "failed" } , { logger .format_time_difference (run_start_time , datetime .datetime .now ())} ' ))
135- event_queue .put (events .NodeFinished (
138+ event_queue .put (pipeline_events .NodeFinished (
136139 node_path = running_pipeline .path (), start_time = start_time ,
137140 end_time = datetime .datetime .now (), is_pipeline = True , succeeded = succeeded ))
138141 del running_pipelines [running_pipeline ]
139142 processed_nodes .add (running_pipeline )
140143
141144 # announce run start
142- event_queue .put (events .RunStarted (node_path = pipeline .path (), start_time = run_start_time , pid = os .getpid ()))
145+ event_queue .put (pipeline_events .RunStarted (node_path = pipeline .path (),
146+ start_time = run_start_time ,
147+ pid = os .getpid (),
148+ interactively_started = interactively_started ,
149+ node_ids = [node .id for node in (nodes or [])],
150+ is_root_pipeline = (pipeline .parent is None ))
151+ )
143152
144153 # run as long
145154 # - as task processes are still running
@@ -173,8 +182,8 @@ def track_finished_pipelines():
173182 # book keeping and event emission
174183 pipeline_start_time = datetime .datetime .now ()
175184 running_pipelines [next_node ] = [pipeline_start_time , 0 ]
176- event_queue .put (events .NodeStarted (next_node .path (), pipeline_start_time , True ))
177- event_queue .put (events .Output (
185+ event_queue .put (pipeline_events .NodeStarted (next_node .path (), pipeline_start_time , True ))
186+ event_queue .put (pipeline_events .Output (
178187 node_path = next_node .path (), format = logger .Format .ITALICS ,
179188 message = '★ ' + node_cost .format_duration (
180189 node_durations_and_run_times .get (tuple (next_node .path ()), [0 , 0 ])[0 ])))
@@ -190,13 +199,13 @@ def track_finished_pipelines():
190199 queue ([sub_pipeline ])
191200
192201 except Exception as e :
193- event_queue .put (events .NodeStarted (
202+ event_queue .put (pipeline_events .NodeStarted (
194203 node_path = next_node .path (), start_time = task_start_time , is_pipeline = True ))
195204 logger .log (message = f'Could not launch parallel tasks' , format = logger .Format .ITALICS ,
196205 is_error = True )
197206 logger .log (message = traceback .format_exc (),
198- format = events .Output .Format .VERBATIM , is_error = True )
199- event_queue .put (events .NodeFinished (
207+ format = pipeline_events .Output .Format .VERBATIM , is_error = True )
208+ event_queue .put (pipeline_events .NodeFinished (
200209 node_path = next_node .path (), start_time = task_start_time ,
201210 end_time = datetime .datetime .now (), is_pipeline = True , succeeded = False ))
202211
@@ -209,8 +218,9 @@ def track_finished_pipelines():
209218 # run a task in a subprocess
210219 if next_node .parent in running_pipelines :
211220 running_pipelines [next_node .parent ][1 ] += 1
212- event_queue .put (events .NodeStarted (next_node .path (), datetime .datetime .now (), False ))
213- event_queue .put (events .Output (
221+ event_queue .put (
222+ pipeline_events .NodeStarted (next_node .path (), datetime .datetime .now (), False ))
223+ event_queue .put (pipeline_events .Output (
214224 node_path = next_node .path (), format = logger .Format .ITALICS ,
215225 message = '★ ' + node_cost .format_duration (
216226 node_durations_and_run_times .get (tuple (next_node .path ()), [0 , 0 ])[0 ])))
@@ -238,12 +248,12 @@ def track_finished_pipelines():
238248
239249 end_time = datetime .datetime .now ()
240250 event_queue .put (
241- events .Output (task_process .task .path (),
242- ('succeeded' if succeeded else 'failed' ) + ', '
243- + logger .format_time_difference (task_process .start_time , end_time ),
244- format = logger .Format .ITALICS , is_error = not succeeded ))
245- event_queue .put (events .NodeFinished (task_process .task .path (), task_process .start_time ,
246- end_time , False , succeeded ))
251+ pipeline_events .Output (task_process .task .path (),
252+ ('succeeded' if succeeded else 'failed' ) + ', '
253+ + logger .format_time_difference (task_process .start_time , end_time ),
254+ format = logger .Format .ITALICS , is_error = not succeeded ))
255+ event_queue .put (pipeline_events .NodeFinished (task_process .task .path (), task_process .start_time ,
256+ end_time , False , succeeded ))
247257
248258 # check if some pipelines finished
249259 track_finished_pipelines ()
@@ -252,8 +262,8 @@ def track_finished_pipelines():
252262 time .sleep (0.001 )
253263
254264 except :
255- event_queue .put (events .Output (node_path = pipeline .path (), message = traceback .format_exc (),
256- format = logger .Format .ITALICS , is_error = True ))
265+ event_queue .put (pipeline_events .Output (node_path = pipeline .path (), message = traceback .format_exc (),
266+ format = logger .Format .ITALICS , is_error = True ))
257267
258268 # run again because `dequeue` might have moved more nodes to `finished_nodes`
259269 track_finished_pipelines ()
@@ -263,27 +273,23 @@ def track_finished_pipelines():
263273 statistics_process .join ()
264274
265275 # run finished
266- event_queue .put (events .RunFinished (node_path = pipeline .path (), end_time = datetime .datetime .now (),
267- succeeded = not failed_pipelines ))
276+ event_queue .put (pipeline_events .RunFinished (node_path = pipeline .path (), end_time = datetime .datetime .now (),
277+ succeeded = not failed_pipelines ,
278+ interactively_started = interactively_started ))
268279
269280 # fork the process and run `run`
270281 run_process = multiprocessing_context .Process (target = run , name = 'pipeline-' + '-' .join (pipeline .path ()))
271282 run_process .start ()
272283
273284 runlogger = run_log .RunLogger ()
274- event_handlers = [runlogger ]
275-
276- # todo: make event handlers configurable (e.g. for slack)
277- if config .slack_token ():
278- event_handlers .append (slack .Slack ())
279285
280286 # process messages from forked child processes
281287 while True :
282288 try :
283289 while not event_queue .empty ():
284290 event = event_queue .get (False )
285- for event_handler in event_handlers :
286- event_handler . handle_event (event )
291+ runlogger . handle_event ( event )
292+ events . notify_configured_event_handlers (event )
287293 yield event
288294 except queues .Empty :
289295 pass
@@ -294,10 +300,10 @@ def track_finished_pipelines():
294300 # Catching GeneratorExit needs to end in a return!
295301 return
296302 except :
297- output_event = events .Output (node_path = pipeline .path (), message = traceback .format_exc (),
298- format = logger .Format .ITALICS , is_error = True )
299- for event_handler in event_handlers :
300- event_handler . handle_event (output_event )
303+ output_event = pipeline_events .Output (node_path = pipeline .path (), message = traceback .format_exc (),
304+ format = logger .Format .ITALICS , is_error = True )
305+ runlogger . handle_event ( output_event )
306+ events . notify_configured_event_handlers (output_event )
301307 yield output_event
302308 run_log .close_open_run_after_error (runlogger .run_id )
303309 return
0 commit comments