def run_thread_loop(self): # HACK: Here we need to use the token_utils.parse_name, since the actual # event handler uses the context of the root_event, and that one is bounded # to the [root process] task. event_name_parsed = token_utils.parse_name(self.root_event.context, self.root_event.task.name) LOG.info(yellow("Run ") + yellow(event_name_parsed, bold=True)) # FIXME: implement a decent test try: params = token_utils.matches( self.execution_message_event.re_expressions, event_name_parsed) for event_data in self.execution_message_event.code( self.root_event.context, *params): self.enqueue_event(event=self.root_event.task, event_data=event_data) except Exception as e: LOG.error(red("Failed ") + red(event_name_parsed, bold=True)) LOG.error(e) self.future.set_exception(e) else: LOG.info(green("Done ") + green(event_name_parsed, bold=True)) self.future.set_result("__done")
def raise_unhandled_exception(task_error: TaskError): log_path = get_folder(task_error.failed_event) LOG.error( red("Process execution failed. Unhandled error from ") + red(str(task_error.failed_event), bold=True) ) if logredirect.is_enabled(): stdout_file = os.path.join(log_path, "stdout") if os.path.isfile(stdout_file): with open(stdout_file) as f: LOG.error(white("STDOUT:", bold=True)) LOG.error(white(f.read())) else: LOG.error(white("STDOUT:", bold=True) + white(" not found")) stderr_file = os.path.join(log_path, "stderr") if os.path.isfile(stderr_file): with open(stderr_file) as f: LOG.error(red("STDERR:")) LOG.error(red(f.read())) else: LOG.error(red("STDERR:", bold=True) + red(" not found")) LOG.error(red("Exception:", bold=True)) LOG.error(red(task_error.error)) sys.exit(1)
def deep_copy_event(e: ActiveEvent) -> ActiveEvent: """ We deepcopy everything except the workspace. :param e: :return: """ try: workspace = e.context.workspace e.context.workspace = None result = copy.deepcopy(e) result.context.workspace = workspace return result except Exception as err: LOG.error(red("Unable to serialize token", bold=True)) LOG.error(red(f"Data: {e.context.data._data}")) raise err
def log_running_done(event: ActiveEvent, task_error=None): if event.loop_type in (ActiveLoopType.INITIAL, ActiveLoopType.INITIAL_EMPTY): return if not task_error: LOG.info(green("Done ") + green(event.context.task_name, bold=True)) return if task_error.failed_event != event: LOG.info( red("Terminated ") + red(event.context.task_name, bold=True) + red(" reason: ") + red(str(task_error.failed_event), bold=True)) return LOG.info(red("Failed ") + red(event.context.task_name, bold=True))
def running_event(self, event: ActiveEvent, data: Any) -> None: if event.loop_type == ActiveLoopType.INITIAL: loop_controller.evaluate_initial_loop(event, self.clone_event) if event.loop_type == ActiveLoopType.INITIAL_EMPTY: self.events.transition( event=event, state=ActiveEventState.ROUTING, data=event.context ) else: self.events.transition(event=event, state=ActiveEventState.DONE) return if ( event.deduplication_id is not None and event is self.events.get_waiting_deduplication(event=event) ): self.events.clear_waiting_deduplication(event=event) if ( event.deduplication_id and isinstance(event.task, ProcessTask) and cast(ProcessTask, event.task).deduplicate and self.events.get_running_deduplication_event_count(event=event) > 1 ): raise Exception(f"A deduplicated event is already running for {event}") # Since the data is potentially updated in WAIT, we need to ensure # the title matches the current data. event.context._update_title_from_data() # FIXME: probably this try/except should be longer than just the LOG try: LOG.info(yellow("Run ") + yellow(event.context.task_name, bold=True)) except Exception as e: raise Exception(f"Failure on {event.context.task_name}", e) # When we start running, we must register now timer events against the # schedule if isinstance(event.task, ProcessTask) and event.task.timer_events: timers: Set[ActiveTimer] = set() self.active_timers[event.token_id] = timers for timer_event in event.task.timer_events: timers.add( create_active_timer( fire_timer=self.fire_timer, parent_token=event, boundary_event_definition=timer_event, ) ) if isinstance(event.task, Process): for start_task in event.task.start_events.values(): if isinstance(start_task, ProcessTask) and start_task.loop: # we start a loop by firing the loop events, and consume this event. loop_controller.create_loop( event, self.clone_event, start_task, parent_id=event.token_id ) else: self.clone_event(event, start_task, parent_id=event.token_id) return None if isinstance(event.task, Task): if event.task.id not in self.tasks_impl: error_message = ( f"BUG: Task id {event.task.id} ({event.task.name}) " f"not found in implementations {self.tasks_impl}" ) LOG.critical(red(error_message, bold=True)) raise Exception(error_message) future: Future[ExecutionToken] = self.pool.schedule( self.tasks_impl[event.task.id].invoke, args=(copy_event(event),) ) self.assign_event_future(event, future) return None if isinstance(event.task, ScriptTask): future = self.pool.schedule(call_script_task, args=(copy_event(event),)) self.assign_event_future(event, future) return None if isinstance(event.task, UserTask): future = Future() self.assign_event_future(event, future) assert self.ut_provider self.ut_provider.register_event(self, event) return None self.events.transition( event=event, state=ActiveEventState.ROUTING, data=event.context, )
def _print_process_task_mappings(self, *, process: Process, indent=0) -> None: print( "{indent}{type} {name} ({id})".format( indent=" " * indent, type=green("process") if process is self.adhesive_process.process else green("sub-process"), name=yellow(process.name, bold=True), id=white(process.id), ) ) indent += 1 for task_id, task in process.tasks.items(): task_impl = self.tasks_impl.get(task_id, None) if isinstance(task, Process): self._print_process_task_mappings(process=task, indent=indent + 1) continue if isinstance(task, Event) and not isinstance(task, MessageEvent): continue if isinstance(task, Gateway): continue if isinstance(task, ScriptTask): print( "{indent}{type} {name} ({id}) -> {none}".format( indent=" " * indent, type=green("script"), name=yellow(task.name, bold=True), id=white(task.id), none=cyan("<bpmn embedded script>", bold=True), ) ) continue if not task_impl: print( "{indent}{type} {name} ({id}) -> {none}".format( indent=" " * indent, type=green("task"), name=yellow(task.name, bold=True), id=white(task.id), none=red("NONE", bold=False), ) ) continue task_type = "task" if isinstance(task, ComplexGateway): task_type = "gateway" elif isinstance(task, UserTask): task_type = "user task" print( "{indent}{type} {name} ({id}) -> {fn} ({file})".format( indent=" " * indent, type=green(task_type), name=yellow(task.name, bold=True), id=white(task.id), file=inspect.getfile(task_impl.code), fn=cyan(task_impl.code.__name__, bold=True), ) )