def run(self): if self._latch and not self._stopped_or_interrupted: self._state_change(self._latch_wait_state) # TODO Race condition? self._latch.wait() # Is variable assignment atomic? Better be sure with lock: # https://stackoverflow.com/questions/2291069/is-python-variable-assignment-atomic with self._executing_flag_lock: self._executing = not self._stopped_or_interrupted if not self._executing: self._state_change(ExecutionState.CANCELLED) return try: if self._no_overlap and any(j for j in taro.client.read_jobs_info() if j.job_id == self.job_id and j.instance_id != self.instance_id): self._state_change(ExecutionState.SKIPPED) return except Exception as e: log.warning("event=[read_jobs_info_error] error=[%s]", e) self._state_change(ExecutionState.TRIGGERED if self._execution. is_async else ExecutionState.RUNNING) try: new_state = self._execution.execute() self._state_change(new_state) except Exception as e: exec_error = e if isinstance( e, ExecutionError) else ExecutionError.from_unexpected_error(e) self._state_change(exec_error.exec_state, exec_error)
def to_job_info(t): state_changes = ((ExecutionState[state], datetime.datetime.fromtimestamp(changed, tz=timezone.utc)) for state, changed in json.loads(t[4])) lifecycle = ExecutionLifecycle(*state_changes) warnings = json.loads(t[6]) if t[6] else dict() exec_error = ExecutionError( t[7], lifecycle.state()) if t[7] else None # TODO more data return JobInfo(t[0], t[1], lifecycle, t[5], warnings, exec_error)
def to_job_info(as_dict) -> JobInfo: state_changes = ((ExecutionState[state_change['state']], util.dt_from_utc_str(state_change['changed'])) for state_change in as_dict['lifecycle']['state_changes']) lifecycle = ExecutionLifecycle(*state_changes) if as_dict['exec_error']: exec_error = ExecutionError(as_dict['exec_error']['message'], ExecutionState[as_dict['exec_error']['state']]) else: exec_error = None return JobInfo(JobInstanceID(as_dict['id']['job_id'], as_dict['id']['instance_id']), lifecycle, as_dict['status'], as_dict['warnings'], exec_error, **as_dict['params'])
def execute(self) -> ExecutionState: ret_code = -1 if not self._stopped and not self._interrupted: stdout = PIPE if self.read_output else None stderr = STDOUT if self.read_output else None try: self._popen = Popen( " ".join(self.args) if USE_SHELL else self.args, stdout=stdout, stderr=stderr, shell=USE_SHELL) output_reader = None if self.read_output: output_reader = Thread(target=self._read_output, name='Output-Reader', daemon=True) output_reader.start() # print(psutil.Process(self.popen.pid).memory_info().rss) ret_code = self._popen.wait() if output_reader: output_reader.join(timeout=1) if ret_code == 0: return ExecutionState.COMPLETED except KeyboardInterrupt: return ExecutionState.STOPPED except FileNotFoundError as e: sys.stderr.write(str(e) + "\n") raise ExecutionError(str(e), ExecutionState.FAILED) from e except SystemExit as e: raise ExecutionError('System exit', ExecutionState.INTERRUPTED) from e if self._stopped: return ExecutionState.STOPPED if self._interrupted: return ExecutionState.INTERRUPTED raise ExecutionError("Process returned non-zero code " + str(ret_code), ExecutionState.FAILED)
def execute(self) -> ExecutionState: if not self._stopped and not self._interrupted: self._process = Process(target=self._run) self._process.start() output_reader = Thread(target=self._read_output, name='Output-Reader', daemon=True) output_reader.start() self._process.join() self.output_queue.put_nowait(_QueueStop()) output_reader.join(timeout=1) self.output_queue.close() if self._process.exitcode == 0: return ExecutionState.COMPLETED if self._stopped: return ExecutionState.STOPPED if self._interrupted: return ExecutionState.INTERRUPTED raise ExecutionError( "Process returned non-zero code " + str(self._process.exitcode), ExecutionState.FAILED)