def test_workflow_thread_cancel_external(self): with TestAreaContext( "python/job_queue/workflow_runner_external") as work_area: WorkflowCommon.createWaitJob() joblist = WorkflowJoblist() self.assertTrue(joblist.addJobFromFile("WAIT", "external_wait_job")) self.assertTrue("WAIT" in joblist) workflow = Workflow("wait_workflow", joblist) self.assertEqual(len(workflow), 3) workflow_runner = WorkflowRunner(workflow, ert=None, context=SubstitutionList()) self.assertFalse(workflow_runner.isRunning()) with workflow_runner: wait_until( lambda: self.assertTrue(workflow_runner.isRunning())) wait_until(lambda: self.assertFileExists("wait_started_0")) wait_until(lambda: self.assertFileExists("wait_finished_0")) wait_until(lambda: self.assertFileExists("wait_started_1")) workflow_runner.cancel() self.assertTrue(workflow_runner.isCancelled()) self.assertFileDoesNotExist("wait_finished_1") self.assertFileDoesNotExist("wait_started_2") self.assertFileDoesNotExist("wait_cancelled_2") self.assertFileDoesNotExist("wait_finished_2")
def test_workflow_thread_cancel_ert_script(self): with TestAreaContext( "python/job_queue/workflow_runner_ert_script") as work_area: WorkflowCommon.createWaitJob() joblist = WorkflowJoblist() self.assertTrue(joblist.addJobFromFile("WAIT", "wait_job")) self.assertTrue("WAIT" in joblist) workflow = Workflow("wait_workflow", joblist) self.assertEqual(len(workflow), 3) workflow_runner = WorkflowRunner(workflow) self.assertFalse(workflow_runner.isRunning()) workflow_runner.run() self.assertIsNone(workflow_runner.workflowResult()) time.sleep(1) # wait for workflow to start self.assertTrue(workflow_runner.isRunning()) self.assertFileExists("wait_started_0") time.sleep(1) # wait for first job to finish workflow_runner.cancel() time.sleep(1) # wait for cancel to take effect self.assertFileExists("wait_finished_0") self.assertFileExists("wait_started_1") self.assertFileExists("wait_cancelled_1") self.assertFileDoesNotExist("wait_finished_1") self.assertTrue(workflow_runner.isCancelled()) workflow_runner.wait() # wait for runner to complete self.assertFileDoesNotExist("wait_started_2") self.assertFileDoesNotExist("wait_cancelled_2") self.assertFileDoesNotExist("wait_finished_2")
def test_workflow_thread_cancel_ert_script(self): with TestAreaContext("python/job_queue/workflow_runner_ert_script"): WorkflowCommon.createWaitJob() joblist = WorkflowJoblist() self.assertTrue(joblist.addJobFromFile("WAIT", "wait_job")) self.assertTrue("WAIT" in joblist) workflow = Workflow("wait_workflow", joblist) self.assertEqual(len(workflow), 3) workflow_runner = WorkflowRunner(workflow) self.assertFalse(workflow_runner.isRunning()) with workflow_runner: self.assertIsNone(workflow_runner.workflowResult()) wait_until(lambda: self.assertTrue(workflow_runner.isRunning())) wait_until(lambda: self.assertFileExists("wait_started_0")) wait_until(lambda: self.assertFileExists("wait_finished_0")) wait_until(lambda: self.assertFileExists("wait_started_1")) workflow_runner.cancel() wait_until(lambda: self.assertFileExists("wait_cancelled_1")) self.assertTrue(workflow_runner.isCancelled()) self.assertFileDoesNotExist("wait_finished_1") self.assertFileDoesNotExist("wait_started_2") self.assertFileDoesNotExist("wait_cancelled_2") self.assertFileDoesNotExist("wait_finished_2")
class RunWorkflowWidget(QWidget): workflowSucceeded = Signal(list) workflowFailed = Signal() workflowKilled = Signal() def __init__(self, ert): self.ert = ert QWidget.__init__(self) layout = QHBoxLayout() layout.addSpacing(10) self._workflow_combo = QComboBox() addHelpToWidget(self._workflow_combo, "run/workflow") self._workflow_combo.addItems( sorted(ert.getWorkflowList().getWorkflowNames(), key=str.lower)) layout.addWidget(QLabel("Select Workflow:"), 0, Qt.AlignVCenter) layout.addWidget(self._workflow_combo, 0, Qt.AlignVCenter) # simulation_mode_layout.addStretch() layout.addSpacing(20) self.run_button = QToolButton() self.run_button.setIconSize(QSize(32, 32)) self.run_button.setText("Start Workflow") self.run_button.setIcon(resourceIcon("play_circle.svg")) self.run_button.clicked.connect(self.startWorkflow) self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) layout.addWidget(self.run_button) layout.addStretch(1) self.setLayout(layout) self._running_workflow_dialog = None self.workflowSucceeded.connect(self.workflowFinished) self.workflowFailed.connect(self.workflowFinishedWithFail) self.workflowKilled.connect(self.workflowStoppedByUser) self._workflow_runner = None """:type: WorkflowRunner""" def createSpinWidget(self): widget = QWidget() layout = QHBoxLayout() size = 64 spin_movie = resourceMovie("loading.gif") spin_movie.setSpeed(60) spin_movie.setScaledSize(QSize(size, size)) spin_movie.start() processing_animation = QLabel() processing_animation.setMaximumSize(QSize(size, size)) processing_animation.setMinimumSize(QSize(size, size)) processing_animation.setMovie(spin_movie) layout.addWidget(processing_animation) processing_label = QLabel( f"Processing workflow '{self.getCurrentWorkflowName()}'") layout.addWidget(processing_label, Qt.AlignBottom) widget.setLayout(layout) return widget def cancelWorkflow(self): if self._workflow_runner.isRunning(): cancel = QMessageBox.question( self, "Confirm Cancel", "Are you sure you want to cancel the running workflow?", QMessageBox.Yes | QMessageBox.No, ) if cancel == QMessageBox.Yes: self._workflow_runner.cancel() self._running_workflow_dialog.disableCloseButton() def getCurrentWorkflowName(self): index = self._workflow_combo.currentIndex() return sorted(self.ert.getWorkflowList().getWorkflowNames(), key=str.lower)[index] def startWorkflow(self): self._running_workflow_dialog = WorkflowDialog("Running Workflow", self.createSpinWidget(), self) self._running_workflow_dialog.closeButtonPressed.connect( self.cancelWorkflow) workflow_thread = Thread(name="ert_gui_workflow_thread") workflow_thread.setDaemon(True) workflow_thread.run = self.runWorkflow workflow_list = self.ert.getWorkflowList() workflow = workflow_list[self.getCurrentWorkflowName()] context = workflow_list.getContext() self._workflow_runner = WorkflowRunner(workflow, self.ert, context) self._workflow_runner.run() workflow_thread.start() self._running_workflow_dialog.show() def runWorkflow(self): while self._workflow_runner.isRunning(): time.sleep(2) cancelled = self._workflow_runner.isCancelled() if cancelled: self.workflowKilled.emit() else: success = self._workflow_runner.workflowResult() if success: report = self._workflow_runner.workflowReport() failed_jobs = [ k for k, v in report.items() if not v["completed"] ] self.workflowSucceeded.emit(failed_jobs) else: self.workflowFailed.emit() def workflowFinished(self, failed_jobs): workflow_name = self.getCurrentWorkflowName() jobs_msg = "successfully!" if failed_jobs: jobs_msg = "\nThe following jobs failed: " + ", ".join( [j for j in failed_jobs]) QMessageBox.information( self, "Workflow completed!", f"The workflow '{workflow_name}' completed {jobs_msg}", ) self._running_workflow_dialog.accept() self._running_workflow_dialog = None def workflowFinishedWithFail(self): workflow_name = self.getCurrentWorkflowName() error = self._workflow_runner.workflowError() QMessageBox.critical( self, "Workflow failed!", f"The workflow '{workflow_name}' failed!\n\n{error}", ) self._running_workflow_dialog.reject() self._running_workflow_dialog = None def workflowStoppedByUser(self): workflow_name = self.getCurrentWorkflowName() QMessageBox.information( self, "Workflow killed!", f"The workflow '{workflow_name}' was killed successfully!", ) self._running_workflow_dialog.reject() self._running_workflow_dialog = None