Why Does Withcallinghandlers Still Stops Execution

Why does withCallingHandlers still stops execution?

Calling handlers provide a way of 'touching' a condition on the way through, maybe logging an error to a file before signalling the user in an interactive session.

Calling handlers can be used to 'muffle' warnings, messages, or errors if the calling handler doesn't actually return. You can make a calling handler not return using restarts -- surround the code that you'd like to continue executing in a call to withRestarts(), and invoke the restart in the handler:

f2 <- function() {
cat("before tryCatch\n")
withCallingHandlers({
withRestarts({
stop("this is an error!")
}, muffleStop=function() {
message("'stop' muffled")
})
},
error = function(cond) {
print(cond$message)
invokeRestart("muffleStop")
}
)
cat("after tryCatch\n")
}

It's more usual that the restarts are established in one chunk of code (like in the built-in function warning) and invoked in a completely independent chunk of code (like the built-in function suppressWarnings:

> warning
function (..., call. = TRUE, immediate. = FALSE, noBreaks. = FALSE,
domain = NULL)
{
##
## ...
##
withRestarts({
.Internal(.signalCondition(cond, message, call))
.Internal(.dfltWarn(message, call))
}, muffleWarning = function() NULL)
##
## ...
##
}
<bytecode: 0x51a4730>
<environment: namespace:base>
> suppressWarnings
function (expr)
{
ops <- options(warn = -1)
on.exit(options(ops))
withCallingHandlers(expr,
warning = function(w) invokeRestart("muffleWarning"))
}
<bytecode: 0x35c2a60>
<environment: namespace:base>

`withCallingHandlers` inside `tryCatch` creates uncatchable error

This should tell you what is happening with your definition of warning_to_error:

> tryCatch(warning_to_error(warning('foobar')), condition = print)
<simpleWarning in withCallingHandlers(expr, warning = stop): foobar>```

As the documentation for stop says, when you call stop with a condition, that condition is signalled to look for handlers, which means warning handlers in this case. If you want an error handler to be invoked you need to signal an error condition. That is what happens when you set options(warn = 2) for example. So you need something like

warning_to_error1 <- function (expr)
withCallingHandlers(expr,
warning = function(w)
stop("(converted from warning) ",
conditionMessage(w)))

That gives you

> tryCatch(warning_to_error1(warning('foobar')),
+ error = function(e) print("Got it"))
[1] "Got it"

Ideally we should provide a condition class and constructor for warnings converted to errors and use that internally for warn = 2

Can't resolve error in .Rmd file Anonymous ... withCallingHandlers - withVisible - eval - eval -

Don't compile with the button in rstudio. Try:

library("knitr")
knit2html("file")

How to continue function when error is thrown in withCallingHandlers in R

You can just wrap each call to testingFunction with a call to tryCatch.:

counter <- 0
testForExpectedError <- function(expr) {
tryCatch(expr, error=function(err) {
print(paste(err$message))
if(err$message == paste("The function should throw this error message",
"at the right time.")){
counter <<- counter + 1
}
})
}

testForExpectedError(testingFunction(df0, df1))
testForExpectedError(testingFunction(df2, df3))
testForExpectedError(testingFunction(df4, df5))

stopifnot(counter == 2)

Handling of condition objects in R

In my experience, tryCatch is used to catch and either ignore or specifically handle errors and warnings. While you can do warnings with this function, I see it more often used with withCallingHandlers (and invokeRestart("muffleWarning")).

The Advanced R book is a good reference for many topics; for this, I'd recommend two chapters:

  • Exceptions and debugging, where Hadley highlights one key difference:

    withCallingHandlers() is a variant of tryCatch() that establishes local handlers, whereas tryCatch() registers exiting handlers. Local handlers are called in the same context as where the condition is signalled, without interrupting the execution of the function. When a exiting handler from tryCatch() is called, the execution of the function is interrupted and the handler is called. withCallingHandlers() is rarely needed, but is useful to be aware of.

    Bold emphasis is mine, to highlight that tryCatch interrupts, withCallingHandlers does not. This means that when a warning is raised, nothing else is executed:

    tryCatch({warning("foo");message("bar");}, warning=function(w) conditionMessage(w))
    # [1] "foo"
    tryCatch({warning("foo");message("bar");})
    # Warning in tryCatchList(expr, classes, parentenv, handlers) : foo
    # bar
  • Further details/examples are in the Beyond exception handling chapter.

tryCatch executes the expression in a context where anything "raised" is optionally caught and potentially altered, ignored, logged, etc. A common method I see is to accept any error and return a known entity (now without an error), such as

ret <- tryCatch(
something_that_fails(),
error = function(e) FALSE)

Unlike other operating systems that allow precise filters on what to handle (e.g., python and its try: ... except ValueError: syntax, ref: https://docs.python.org/3/tutorial/errors.html#handling-exceptions), R is a bit coarser in that it catches all errors you can get to figure out what kind of error it is.

If you look at the source of tryCatch and trace around its use of its self-defined functions, you'll see that ultimately it calls an R function .addCondHands that includes the list of handlers, matching on the names of the handlers (typically error and warning, though perhaps others might be used?). (The source for that function can be found here, though I don't find it very helpful in this context).

I don't know exactly how to answer "how exactly ... get to its handler", other than an exception is thrown, it is caught, and the handler is executed with the error object as an argument.

Handling errors before warnings in tryCatch

This relates to the "is it possible to process the warning and then have the function continue while still catching errors?" question.

I had a similar issue where I wanted to bind my logging functions for warnings and errors to the try catch and always continue after warnings and also be able to perform multiple attempts at try catch, e.g. for accessing a flimsy network drive. This is what I ended up using. This or a more simplified version could help with what your after.

MyLibrary.Sys.Try <- function(
expr, # Expression that will be evaluated by the call to Try
errorMessage="", # Optional prepended string to add to error output
warningMessage="", # Optional prepended string to add to warning output
failureMessage="", # Optional prepended string to add to failing all attempts
finally=NULL, # Optional expression to be executed at the end of tryCatch
attempts=1, # Number of attempts to try evaluating the expression
suppressError=TRUE, # Should the call just log the error or raise it if all attempts fail
quiet=FALSE # Return expression normally or as invisible
) {
tryNumber <- 0
while(tryNumber<attempts) {
tryNumber <- tryNumber + 1

# If not suppressing the error and this is the last
# attempt then just evaluate the expression straight out
if(tryNumber == attempts && !suppressError){
# NOTE: I think some warnings might be lost here when
# running in non-interactive mode. But I think it should be okay
# because even nested dispatch seems to pick them up:
# MyLibrary.Sys.Try(MyLibrary.Sys.Try(function(),suppressError=F))
return(expr)
}

tryCatch({
# Set the warning handler to an empty function
# so it won't be raised by tryCatch but will
# be executed by withCallingHandlers
options(warning.expression=quote(function(){}))
withCallingHandlers({
if(quiet) {
return(invisible(expr))
} else {
return(expr)
}
},error=function(ex){
MyLibrary.Sys.Log.Error(errorMessage,ex)
}, warning=function(warn){
# Had issues with identical warning messages being
# issued here so to avoid that only log it the first
# time it happens in a given minute.
warnStamp <- paste(Sys.time(),warn,collapse="_",sep="")
if(!identical(warnStamp,getOption("MyLibrary.LAST.WARNING"))) {
if(!(interactive() && is.null(getOption("warning.expression")))){
MyLibrary.Sys.Log.Warning(warningMessage,warn)
}
options(MyLibrary.LAST.WARNING=warnStamp)
}
})
},error=function(ex){
# Suppressing the error since it's been logged
# by the handler above. Needs to be suppressed
# to not invoke the stop directly since the
# handler above passes it through.
},finally={
# Set the warning handler to the default value
# of NULL which will cause it to revert to it's
# normal behaviour. If a custom handler is normally
# attached it would make sense to store it above
# and then restore it here. But don't need that now
options(warning.expression=NULL)
if(!is.null(finally)){
if(quiet) {
return(invisible(finally))
} else {
return(finally)
}
}
})
}

msg <- paste(ifelse(nchar(failureMessage)>0," - ",""),"Failed to call expression after ",attempts," attempt(s)",sep="")
MyLibrary.Sys.Log.Error(failureMessage,msg)
}


Related Topics



Leave a reply



Submit