composerr_flush: Halting and flushing errors

View source: R/composerr.R

composerr_haltR Documentation

Halting and flushing errors

Description

The following functions allow the accumulation of multiple errors in an internal error stack of an error handler err_h and flushing them all at once later on:

  • composerr_halt(err_h): Set the error handling function err_h1 to non-flushing mode, which means that each time err_h1(msg) is called, the error message (msg) gets accumulated in an internal error stack, but no error is thrown.

  • composerr_flush(err_h): If the internal error stack is non-empty, then create the full error message from the internal error stack and throw an error. If the internal error stack is empty, then do nothing.

  • composerr_counterr(err_h): Counts the number of times err_h1 was called, since composerr_halt(err_h1) was called.

Usage

composerr_halt(err_h)

composerr_flush(err_h, action = NULL, ...)

composerr_counterr(err_h)

Arguments

err_h

An error handler created with composerr()

action

Optional default ultimate error handler. This argument can either be NULL (no default handler defined) or a function function(msg, ...). In the latter case this defines a default function that will be used as ultimate error handler if the current error handler is called without passing an action argument. The used ultimate error handler is passed down the error handler cascade until it ultimately will be called in order to process the successively extended error message (e.g. throw an error or a warning or do something else with the created error message). The argument action can be used, in order to change the default behavior of the error processing to throwing a warning (action = warning) or some other customized behavior like writing the error message to a text file (action = function(msg, ...) cat(msg, file = FILENAME, fill = TRUE)).

...

Additional arguments, which will be passed down the error handler cascade and ultimately be passed to the ultimate error handler action(msg_full, ...).

Value

composerr_halt() silently returns the passed in error handler err_h and composerr_flush() silently returns the return value of the ultimate error handler action.

Cascading error handlers

If err_h_new <- composerr(...) was created without passing a parent error handler to the optional argument err_h, then err_h_new is a newly created error handler with the following structure err_h_new = function(msg = NULL, action = NULL, ...). Similarly, if err_h_new <- composerr(err_h = err_h_parent, ...) was created by passing a parent error handler err_h = err_h_parent, then err_h_new is also a new error handler, which also has the structure err_h_new = function(msg = NULL, action = NULL, ...), but now it was created from a parent error handler err_h_parent.

We now consider the latter case err_h_new <- composerr(before, err_h_parent, after, action = action_default). If the error handler gets executed with the following call err_h_new(msg, action = action_1, ...), then the following things will happen:

  • if the argument collapse in composerr(...) was set to a string, then err_h_new will first create a more detailed message, by first adding the text parts defined in the arguments before and after and then collapsing the created character vector using the string in collapse as separator msg_new <- paste(paste0(before, msg, after), collapse = collapse)

  • if the argument collapse in composerr(...) was omitted or set to NULL, then err_h_new will first create a more detailed message, by first adding the text parts defined in the arguments before and after, but without collapsing the resulting vector: msg_new <- paste0(before, msg, after)

  • if no argument action is passed to err_h_new (in this example action_1 = NULL), then action will be set to the default ultimate handler (in this example action_default) previously assigned to err_h_new with the argument action. If no default ultimate handler was previously assigned to err_h_new, then action will be set to NULL.

  • Finally, err_h_new will call err_h_parent(msg_new, action = action, ...)

Since err_h_parent is called at the end and err_h_parent will run through the same process, maybe calling its parent error handler, we have a so called cascade of successive error handler calls. The following arguments are passed down this error handler cascade:

  • a successively growing error message msg (incrementally adding text parts in front and at the end of the message).

  • the argument action, which can either be NULL or an ultimate error handling function used for ultimate processing of the created error message (throw an error or a warning or do something else). If the passed down action argument is NULL and at some point in the error handling cascade there was a non-null default ultimate error handler assigned to some error handler in the error handler cascade, then this default ultimate error handler is passed on instead as argument action. Once the ultimate error handler action is not NULL any more, it will remain unchanged, no matter if there were other default ultimate error handlers assigned to other error handlers in the error handler cascade. This means the default ultimate error handlers of the child error handlers overwrite the default ultimate error handlers of its ancestor error handlers.

  • additional arguments ... passed to the call err_h_new(msg, action, ...) will be directly passed down the error handler cascade and directly passed to the ultimate call of the ultimate error handler action(msg_complete, ...).

After cascading backwards the ancestry of err_h_new, we ultimately reach the primal error handler, which was created first (from scratch) and has no further parent error handler. Let us call it err_h_primal. This primal error handler will do the following things:

  • As usual err_h_primal will concatenate its additional text parts before and after to the passed in message. Also it will maybe collapse the resulting character vector, depending if collapse was set to NULL or not. This will give us at last the ultimate version of the error message. Let us call it msg_complete.

  • As usual err_h_primal checks if action is NULL. If so, action is set to the default handler that was assigned to err_h_primal.

  • If the action is still NULL, then action is replaced by the stop() function.

  • Ultimately err_h_primal calls action(msg_complete, ...).

The result of the final call action(msg_complete, ...) is silently returned by err_h_new.

Stacked error messages

Let us assume composerr_halt(err_h_new) was called before calling err_h_new(msg, ...). In this case the error message will not be cascaded down and action will not be called. Instead err_h_new will do the following things:

  • As usual err_h_new will concatenate the additional message parts: msg_new <- paste0(before, msg, after)

  • err_h_new will append the resulting error message msg_new to an internal error stack of err_h_new. err_stack <- c(err_stack, msg_new)

  • err_h_new will return the value NULL

If later on, the function composerr_flush(err_h_new, action = action4, ...) is called, then the error handler cascade will be executed as usual, but first err_h_new will do the following things:

  • If collapse != NULL was assigned to err_h_new, then the entire internal error stack (a character vector holding the stored error messages), will be collapsed to a single string: msg_new <- paste(err_stack, collapse = collapse).

  • If collapse == NULL was omitted when calling err_h_new <- composerr(...), then the internal error stack (a character vector holding the stored error messages) of err_h_new will be used as msg_new, without collapsing it first.

  • As usual, if the argument action = action4 is NULL, then it will be replaced by the default ultimate handler assigned to err_h_new (in this example action_default).

  • err_h_parent(msg_new, action = action) will be called and the error handler cascade will continue as usual.

It is also possible to halt the error execution in the middle of the error handler cascade, by calling composerr_halt(err_h_middle), where err_h_middle is an error handler, that has at least one child error handler err_h_new. If err_h_new or another offspring error handler of err_h_new gets called, then the error handling will be cascaded down as usual until it reaches err_h_middle. There it will be halted and the created message will appended to the internal error message stack of err_h_middle. Only by calling composerr_flush(err_h_middle) the error handling cascade will be continued.

It is also possible to halt the error execution at multiple points of the error handler cascade. In order to cascade the errors down till the end, each halted error handler must be flushed with composerr_flush().

See Also

composerr(), composerr_get_action() and validate_composerr()

Examples

## Not run: 
##### Example-1 #####
# create general error handler
err_h_parent <- composerr(before = "There are problems:\n")

# create a more precise error handler listing the problems
err_h_child <- composerr("  - problem-", err_h_parent)

# halt `err_h_child` processing in order to collect
# multiple errors
composerr_halt(err_h_child)
err_h_child(1)
err_h_child(2:3)
err_h_child(4)
composerr_flush(err_h_child)
# Error: There are problems:
#   - problem-1
#   - problem-2
#   - problem-3

##### Example-2 #####
# Advanced implementation of vector multiplication
# using a validation routine with **advanced error handling**:
validate_numeric_vec <- function(obj, err_h) {
  obj_name <- deparse(substitute(obj))
  err_h <- composerr(paste0("Invalid argument `", obj_name, "`: "), err_h)
  if (!is.numeric(obj))
    err_h("Not a number.")
  err_h_list <- composerr(err_h = composerr("\n", err_h))
  composerr_halt(err_h_list)
  for (i in seq_along(obj)) {
    err_h_item <- composerr(paste0("  - Item-", i, " is "), err_h)
    if (is.na(obj[i]) && !is.nan(obj[i]))
      err_h_item("NA.")
    if (is.nan(obj[i]))
      err_h_item("NaN.")
    if (is.infinite(obj[i]))
      err_h_item("infinite.")
  }
  composerr_flush(err_h_list)
  invisible(obj)
}
my_vec_mult2 <- function(x, y) {
  err_h <- composerr("In `my_vec_mult2()`: ")
  validate_numeric_vec(x, err_h)
  validate_numeric_vec(y, err_h)
  if (length(x) != length(y))
    err_h("Vectors `x` and `y` have different length.")
  sum(x*y)
}
my_vec_mult2("a", 1:4)
# Error: In `my_vec_mult2()`: Invalid argument `x`: Not a number.
my_vec_mult2(c(1, NA, NaN, Inf, 5), 1:5)
# Error: In `my_vec_mult2()`: Invalid argument `x`:
#   - Item-2 is NA.
#   - Item-3 is NaN.
#   - Item-4 is infinite.
my_vec_mult2(1:5, c(NaN, 2, 3, NA, Inf))
# Error: In `my_vec_mult2()`: Invalid argument `y`:
#   - Item-1 is NA.
#   - Item-4 is NaN.
#   - Item-5 is infinite.
my_vec_mult2(1:5, 1:4)
# Error: In `my_vec_mult2()`: Vectors `x` and `y` have different length.
my_vec_mult2(1:5, 1:5)
55

## End(Not run)

a-maldet/composerr documentation built on Oct. 29, 2022, 8:05 p.m.