def _workflow_wrapper(self, factory, input_data, *extra_args): wf_kwargs = {} for dep_name, proxy in self.proxy_factory_registry.items(): wf_kwargs[dep_name] = proxy(*extra_args) func = factory(**wf_kwargs) try: args, kwargs = self.deserialize_input(input_data) except Exception: logger.exception('Cannot deserialize the input:') raise ValueError('Cannot deserialize the input: %r' % (input_data,)) result = func(*args, **kwargs) # Can't use directly isinstance(result, restart_type) because if the # result is a single result proxy it will be evaluated. This also # fixes another issue, on python2 isinstance() swallows any # exception while python3 it doesn't. if not is_result_proxy(result) and isinstance(result, restart_type): try: traversed_input, (error, placeholders) = traverse_data( [result.args, result.kwargs]) except Exception: logger.exception('Cannot traverse the restart arguments:') raise ValueError( 'Cannot traverse the restart arguments: %r, %r' % result.args, result.kwargs) wait(error) # raise if not None if placeholders: raise SuspendTask r_args, r_kwargs = traversed_input try: serialized_input = self.serialize_restart_input(*r_args, **r_kwargs) except Exception: logger.exception('Cannot serialize the restart arguments:') raise ValueError( 'Cannot serialize the restart arguments: %r, %r' % result.args, result.kwargs) raise Restart(serialized_input) try: traversed_result, (error, placeholders) = traverse_data(result) except Exception: logger.exception('Cannot traverse the result:') raise ValueError('Cannot traverse the result: %r' % result) wait(error) if placeholders: raise SuspendTask try: return self.serialize_result(traversed_result) except Exception: logger.exception('Cannot serialize the result:') raise ValueError('Cannot serialize the result: %r' % (result,))
def __call__(self, *args, **kwargs): node_id = "%s-%s" % (self.trace_name, self.call_number) ((t_args, t_kwargs), (err, results)) = traverse_data( [args, kwargs], f=collect_err_and_results, initial=(None, None) ) r = super(TracingProxy, self).__call__(*t_args, **t_kwargs) assert is_result_proxy(r) factory = r.__factory__ factory.node_id = node_id if err is not None: self.tracer.schedule_activity(node_id, self.trace_name) self.tracer.flush_scheduled() error_factory = err.__factory__ self.tracer.error(node_id, str(error_factory.value)) for dep in results or []: self.tracer.add_dependency(dep.__factory__.node_id, node_id) return r
def __call__(self, *args, **kwargs): """Consult the execution history for results or schedule a new task. This is method gets called from the user workflow code. When calling it, the task it refers to can be in one of the following states: RUNNING, READY, FAILED, TIMEDOUT or NOTSCHEDULED. * If the task is RUNNING this returns a Placeholder. The Placeholder interrupts the workflow execution if its result is accessed by raising a SuspendTask exception. * If the task is READY this returns a Result object. Calling the result method on this object will just return the final value the task produced. * If the task is FAILED this returns an Error object. Calling the result method on this object will raise a TaskError exception containing the error message set by the task. * In case of a TIMEOUT this returns an Timeout object. Calling the result method on this object will raise TaskTimedout exception, a subclass of TaskError. * If the task was NOTSCHEDULED yet: * If any errors in arguments, propagate the error by returning another error. * If any placeholders in arguments, don't do anything because there are unresolved dependencies. * Finally, if all the arguments look OK, schedule it for execution. """ task_exec_history = self.task_exec_history call_number = self.call_number self.call_number += 1 r = placeholder() for retry_number, delay in enumerate(self.retry): if task_exec_history.is_timeout(call_number, retry_number): continue if task_exec_history.is_running(call_number, retry_number): break # result = Placehloder if task_exec_history.has_result(call_number, retry_number): value = task_exec_history.result(call_number, retry_number) order = task_exec_history.order(call_number, retry_number) try: value = self.deserialize_result(value) except Exception as e: logger.exception('Error while deserializing the activity result:') self.task_decision.fail(e) break # result = Placeholder r = result(value, order) break if task_exec_history.is_error(call_number, retry_number): err = task_exec_history.error(call_number, retry_number) order = task_exec_history.order(call_number, retry_number) r = error(err, order) break traversed_args, (err, placeholders) = traverse_data([args, kwargs]) if err: r = copy_result_proxy(err) break if placeholders: break # result = Placeholder t_args, t_kwargs = traversed_args try: input_data = self.serialize_input(*t_args, **t_kwargs) except Exception as e: logger.exception('Error while serializing the task input:') self.task_decision.fail(e) break # result = Placeholder self.task_decision.schedule(call_number, retry_number, delay, input_data) break # result = Placeholder else: # No retries left, it must be a timeout order = task_exec_history.order(call_number, retry_number) r = timeout(order) return r
def __call__(self, *args, **kwargs): """Consult the execution history for results or schedule a new task. This is method gets called from the user workflow code. When calling it, the task it refers to can be in one of the following states: RUNNING, READY, FAILED, TIMEDOUT or NOTSCHEDULED. * If the task is RUNNING this returns a Placeholder. The Placeholder interrupts the workflow execution if its result is accessed by raising a SuspendTask exception. * If the task is READY this returns a Result object. Calling the result method on this object will just return the final value the task produced. * If the task is FAILED this returns an Error object. Calling the result method on this object will raise a TaskError exception containing the error message set by the task. * In case of a TIMEOUT this returns an Timeout object. Calling the result method on this object will raise TaskTimedout exception, a subclass of TaskError. * If the task was NOTSCHEDULED yet: * If any errors in arguments, propagate the error by returning another error. * If any placeholders in arguments, don't do anything because there are unresolved dependencies. * Finally, if all the arguments look OK, schedule it for execution. """ task_exec_history = self.task_exec_history call_number = self.call_number self.call_number += 1 r = placeholder() for retry_number, delay in enumerate(self.retry): if task_exec_history.is_timeout(call_number, retry_number): continue if task_exec_history.is_running(call_number, retry_number): break # result = Placehloder if task_exec_history.has_result(call_number, retry_number): value = task_exec_history.result(call_number, retry_number) order = task_exec_history.order(call_number, retry_number) try: value = self.deserialize_result(value) except Exception as e: logger.exception( 'Error while deserializing the activity result:') self.task_decision.fail(e) break # result = Placeholder r = result(value, order) break if task_exec_history.is_error(call_number, retry_number): err = task_exec_history.error(call_number, retry_number) order = task_exec_history.order(call_number, retry_number) r = error(err, order) break traversed_args, (err, placeholders) = traverse_data([args, kwargs]) if err: r = copy_result_proxy(err) break if placeholders: break # result = Placeholder t_args, t_kwargs = traversed_args try: input_data = self.serialize_input(*t_args, **t_kwargs) except Exception as e: logger.exception('Error while serializing the task input:') self.task_decision.fail(e) break # result = Placeholder self.task_decision.schedule(call_number, retry_number, delay, input_data) break # result = Placeholder else: # No retries left, it must be a timeout order = task_exec_history.order(call_number, retry_number) r = timeout(order) return r