composerr | R Documentation |
composerr()
is used in order to create so called error or warning handlers.
These are mere functions, which have a character argument msg
and maybe
additional arguments ...
and which process this
arguments (e.g. call stop(msg)
).
Often error handling and especially the creation of meaningful error
messages takes a lot of time and often leads to a great amount of duplicate
code, which is often not easy to maintain.
composerr()
offers an easy way out, since it simplifies the creation
of error handlers with meaningful error messages, by incrementally concatenating
more detailed message parts to your error handlers.
The call of composerr()
does two different things, depending on whether
the argument err_h
is used or not (in the following ...
stands for
the arguments before
, after
, collapse
, and action
):
composerr(...)
: Create a new error handling function from scratch.
This error handling function is returned by the call composerr(...)
.
composerr(..., err_h = err_h_parent)
: Create a new (child) error handling
function from another (parent) error handling function err_h_parent
.
This new error handling function enhances err_h_parent
by adding
additional message parts to the message parts defined in err_h_parent
.
The function composerr
returns special composerr class objects,
which are functions with the following structure
function(msg, action = NULL, ...)
. The following arguments are available:
msg
: A character vector passed to the error handler, holding information
about one or multiple errors.
action
: A so called ultimate error handler. This is a
function with the following structure function(msg, ...)
.
The ultimate error handler processes the error message after it was
fully enriched by all message
parts defined in the so called error handler cascade.
...
: Additional arguments passed down the error handler cascade and then
passed to the ultimate error handler defined in action
.
composerr( before = NULL, err_h = NULL, after = NULL, collapse = "\n", action = NULL )
before |
Optional string that will be prepended
to the error message |
err_h |
Optional parent error handler. It must either be |
after |
Optional string that will be appended
to the error message |
collapse |
Optional string that will be used for collapsing the
resulting error message |
action |
Optional default ultimate error handler. This argument
can either be |
The call composerr(...)
always returns a new error handler, either
created from scratch or enhancing a parent error handler err_h_parent
.
The resulting error handler is a function with the following arguments:
msg
: An optional string holding the error message. This error
message will be passed down the error handler cascade and get recursively
concatenated with additional message parts.
action
: An optional ultimate error processing function
(function(msg = NULL, ...)
), which
will be passed down the error handler cascade and ultimately be executed.
...
: Additional argument, which will be passed down the error handler
cascade and ultimately passed to action(msg_full, ...)
.
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
.
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()
.
composerr_flush()
, composerr_halt()
, composerr_counterr()
,
composerr_get_action()
and validate_composerr()
## Not run: #' ##### Example-1: Create error handlers ##### # create new error handler `err_h1()` from scratch err_h1 <- composerr(before = "B1", after = "A1") # `err_h1` throws errors by default err_h1("XXX") # Error: B1XXXA1 # also a vector can be used as message err_h1(c("XXX", "YYY")) # Error: B1XXXA1 # B1YYYA1 # With `action = warning` a warning will be sent instead err_h1("XXX", action = warning) # Warning: B1XXXA1 # create a new error handler `err_h2` based on `err_h1` err_h2 <- composerr( err_h = err_h1, before = "B2", after = "A2", action = message ) # Because of `action = message`, the handler `err_h2` will print # plain messages by default err_h2("XXX") # B1B2XXXA2A1 # The original error handler `err_h1` remained unchanged err_h1("XXX") # Error: B1XXXA1 ##### Example-2: Halt/Flush errors ##### # 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 = err_h_parent) # halt `err_h_child` processing in order to collect multiple errors composerr_halt(err_h_child) err_h_detail(1) err_h_detail(2:3) err_h_detail(4) composerr_flush(err_h_child) # Error: There are problems: # - problem-1 # - problem-2 # - problem-3 # - problem-4 ##### Example-3: Error/Warning handling in functions ##### my_vec_mult <- function(x, y) { # create your error handlers err_h <- composerr("In `my_vec_mult()`: ") err_h_x <- composerr("Invalid argument `x`: ", err_h) err_h_y <- composerr("Invalid argument `y`: ", err_h) if (!is.numeric(x)) err_h_x("Not a number.") if (any(is.na(x))) err_h_x("Has missings.", action = warning) if (!is.numeric(y)) err_h_y("Not a number.") if (any(is.na(y))) err_h_y("Has missings.", action = warning) if (length(x) != length(y)) err_h("Vectors `x` and `y` have different length.") sum(x*y, na.rm = TRUE) } my_vec_mult("a", 1:2) # Error: In `my_vec_mult()`: Invalid argument `x`: Not a number. my_vec_mult(c(1, NA), 1:2) # 1 # Warning: In `my_vec_mult()`: Invalid argument `x`: Has missings. my_vec_mult(1:2, "b") # Error: In `my_vec_mult()`: Invalid argument `y`: Not a number. my_vec_mult(1:2, c(1, NA)) # 1 # Warning: In `my_vec_mult()`: Invalid argument `y`: Has non-finite values. my_vec_mult(1:2, 1:3) # Error: In `my_vec_mult()`: Vectors `x` and `y` have different length. my_vec_mult(1:2, 1:2) # 5 ##### Example-4: Error handling in sub routines ##### 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_list) 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 ##### Example-5: Write errors to log file ##### logfile = tempfile() err_h <- composerr( "There was a problem: ", action = function(msg) { cat(msg, file = logfile, append = TRUE, fill = TRUE) } ) err_h("I am hungry.") err_h("The fridge is empty.") cat(paste(readLines(logfile), collapse = "\n")) # There was a problem: I am hungry. # There was a problem: The fridge is empty. ##### Example-6: Optionally suppressing warnings ##### my_sum <- function(x, suppress_warnings = FALSE) { if (isFALSE(suppress_warnings)) { err_h <- composerr("Problem in `my_sum()`: ", action = warning) } else { # ultimate error handler does nothing err_h <- composerr(action = function(...) {}) } if (any(is.na(x))) err_h("`x` has missing values.") sum(x, na.rm = TRUE) } my_sum(c(1, 2, NA)) # 3 # Warning: Problem in `my_sum()`: `x` has missing values. my_sum(c(1, 2, NA), suppress_warnings = TRUE) # 3 ## End(Not run)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.