Example #1
0
    def save_error(self, context, exception, swallowed):
        """Append step's exception information to the context.

        Append the[on_error] dictionary to the context. This will append to
        existing `runErrors` values if `runErrors` are already in there.

        Args:
            context: (pypyr.context.Context) The pypyr context. This arg will
                     mutate - after method execution will contain the new
                     updated context.
            exception: (Exception) The error detected during step execution.
            swallowed: (bool) Whether exception was swallowed or not.
        """
        failure = {
            'name': get_error_name(exception),
            'description': str(exception),
            'customError': context.get_formatted_iterable(self.on_error)
            if self.on_error else {},
            'line': self.line_no,
            'col': self.line_col,
            'step': self.name,
            'exception': exception,
            'swallowed': swallowed,
        }

        run_errors = context.setdefault('runErrors', [])

        run_errors.append(failure)
Example #2
0
    def exec_iteration(self, counter, context, step_method):
        """Run a single retry iteration.

        This method abides by the signature invoked by poll.while_until_true,
        which is to say (counter, *args, **kwargs). In a normal execution
        chain, this method's args passed by self.retry_loop where context
        and step_method set. while_until_true injects counter as a 1st arg.

        Args:
            counter. int. loop counter, which number of iteration is this.
            context: (pypyr.context.Context) The pypyr context. This arg will
                     mutate - after method execution will contain the new
                     updated context.
            step_method: (method/function) This is the method/function that
                         will execute on every loop iteration. Signature is:
                         function(context)

         Returns:
            bool. True if step execution completed without error.
                  False if error occured during step execution.

        """
        logger.debug("starting")
        context['retryCounter'] = counter
        self.retry_counter = counter

        logger.info("retry: running step with counter %s", counter)
        try:
            step_method(context)
            result = True
        except (ControlOfFlowInstruction, Stop):
            # Control-of-Flow/Stop are instructions to go somewhere
            # else, not errors per se.
            raise
        except Exception as ex_info:
            if self.max:
                if counter == self.max:
                    logger.debug(
                        "retry: max %s retries exhausted. "
                        "raising error.", counter)
                    # arguably shouldn't be using errs for control of flow.
                    # but would lose the err info if not, so lesser of 2 evils.
                    raise

            if isinstance(ex_info, HandledError):
                ex_info = ex_info.__cause__

            if self.stop_on or self.retry_on:
                error_name = get_error_name(ex_info)
                if self.stop_on:
                    formatted_stop_list = context.get_formatted_iterable(
                        self.stop_on)
                    if error_name in formatted_stop_list:
                        logger.error(
                            "%s in stopOn. Raising error "
                            "and exiting retry.", error_name)
                        raise
                    else:
                        logger.debug("%s not in stopOn. Continue.", error_name)

                if self.retry_on:
                    formatted_retry_list = context.get_formatted_iterable(
                        self.retry_on)
                    if error_name not in formatted_retry_list:
                        logger.error(
                            "%s not in retryOn. Raising "
                            "error and exiting retry.", error_name)
                        raise
                    else:
                        logger.debug("%s in retryOn. Retry again.", error_name)

            result = False
            logger.error(
                "retry: ignoring error because retryCounter < max.\n"
                "%s: %s",
                type(ex_info).__name__, ex_info)

        logger.debug("retry: done step with counter %s", counter)

        logger.debug("done")
        return result
Example #3
0
    def run_conditional_decorators(self, context):
        """Evaluate the step decorators to decide whether to run step or not.

        Use pypyr.dsl.Step.run_step if you intend on executing the step the
        same way pypyr does.

        Args:
            context: (pypyr.context.Context) The pypyr context. This arg will
                     mutate.
        """
        logger.debug("starting")

        # The decorator attributes might contain formatting expressions that
        # change whether they evaluate True or False, thus apply formatting at
        # last possible instant.
        run_me = context.get_formatted_as_type(self.run_me, out_type=bool)
        skip_me = context.get_formatted_as_type(self.skip_me, out_type=bool)
        swallow_me = context.get_formatted_as_type(self.swallow_me,
                                                   out_type=bool)

        if run_me:
            if not skip_me:
                try:
                    if self.retry_decorator:
                        self.retry_decorator.retry_loop(
                            context, self.invoke_step)
                    else:
                        self.invoke_step(context=context)
                except (ControlOfFlowInstruction, Stop):
                    # Control-of-Flow/Stop are instructions to go somewhere
                    # else, not errors per se.
                    raise
                except Exception as exc_info:
                    if isinstance(exc_info, HandledError):
                        exc_info = exc_info.__cause__
                    else:
                        # prevent already logged err logging twice.
                        self.save_error(context=context,
                                        exception=exc_info,
                                        swallowed=swallow_me)
                    if swallow_me:
                        logger.error(
                            "%s Ignoring error because swallow "
                            "is True for this step.\n"
                            "%s: %s", self.name, get_error_name(exc_info),
                            exc_info)
                    else:
                        if self.line_no:
                            logger.error(
                                "Error while running step %s "
                                "at pipeline yaml line: %d, col: %d",
                                self.name, self.line_no, self.line_col)
                        else:
                            logger.error("Error while running step %s",
                                         self.name)
                        raise exc_info
            else:
                logger.info("%s not running because skip is True.", self.name)
        else:
            logger.info("%s not running because run is False.", self.name)

        logger.debug("done")
Example #4
0
def test_get_error_name_canonical():
    """Other error returns modulename.name on get_error_name."""
    assert get_error_name(ContextError('blah')) == 'pypyr.errors.ContextError'
Example #5
0
def test_get_error_name_builtin():
    """Builtin returns bare name on get_error_name."""
    assert get_error_name(ValueError('blah')) == 'ValueError'