tryCatch print the expression in the handler - r

Basically, try something as follows:
tryCatch(expr = {stop("stop message")},
error = function(e) {
cat(conditionMessage(e))
cat(as.character(expr))
})
with expect output to be something like: "stop(\"stop message\")", but fails as expr cannot be found... Any way to print expr within the scope without having to do something as follows?
expr <- eval('stop("stop message")')
tryCatch(expr = {expr},
error = function(e) {
cat(conditionMessage(e))
cat(as.character(expr))
})

I'm not sure I'd really recommend this, but you could walk up the call stack to find the tryCatch call and extract the parameter there. Here's a helper function to find a call in the call stack
findStackFun <- function(fun) {
for(cx in sys.calls()) {
if (deparse(cx[[1]]) == fun) {
return(cx)
}
}
return(NULL)
}
Then you could run
tryCatch(expr = {stop("stop message")},
error = function(e) {
cat(conditionMessage(e))
call <- findStackFun("tryCatch")
cat(deparse(call$expr))
})

Related

How to prevent R from stopping when it can't find the file to open?

My R code is trying to open a RDS file in a for loop as follows:
for(i in 1:run_loops){
source("./scripts/load_data.R")
model <- readRDS(file=paste(model_directory,"/",modelname,".Rds", sep="")) #STOPS-HERE!!!
source("./scripts/prediction.R")
}
R stops when there is no model file.
How do I get it to move to the next iteration instead of stopping?
P.S. modelname variable changes each time load_data.R is sourced.
This should do the trick:
for(i in 1:run_loops) {
tryCatch(
expr = {
source("./scripts/load_data.R")
model <-
readRDS(file = paste(model_directory, "/", modelname, ".Rds", sep = "")) #STOPS-HERE!!!
source("./scripts/prediction.R")
},
error = function(e) {
print(paste0(i, ' not done'))
}
)
}
You can use file.exists
file_name <- paste0(model_directory,"/",modelname,".Rds")
if(file.exists(file_name)) {
#do something
} else {
#do something else
}

How to know in trace() exit handler if exception was raised by the function

Is there a way to know in the trace() exit handler if the function raised an unhandled exception? Currently I use geterrmessage(), but it catches handled exceptions in other libraries internals, which is not what I need.
other_silent <- function() try(stop("irrelevant", call. = FALSE), silent = TRUE)
other_error <- function() stop("relevant", call. = FALSE)
my_silent <- function() other_silent()
my_error <- function() other_error()
trace(c("my_silent", "my_error"), print = FALSE,
tracer = quote({.Internal(seterrmessage(""))}),
exit = quote({print(geterrmessage())}))
my_silent()
my_error()
The call to my_silent() produces output:
[1] "Error : irrelevant\n"
While I need it to remain silent, because the function itself finished successfully.
I ended up implementing my own version of trace using code from methods as basis.
mytrace <- function(what) {
where <- environment(sys.function())
def <- getFunction(what, where = where)
body(def, envir = environment(def)) <- rlang::expr({
exception <- NA
tryCatch(!!body(def), error = function(e) {
exception <<- e
stop(e)
}, finally = {<log method with or without exception>})
})
assign(what, def, where)
}

Use of environment in validate_that in R

I am trying to write my own test-function (test_if) that returns both the result of the test as well as an optional error message. The function is based on the validate_that function in the assertthat-package.
The test_if function seems to work, however, I further want to use test_if in a more specific function (check_input) that analyses user-inputs in shiny. There I have a problem, that the check_input-function only works, if I define the test_if function inside the check_input function.
I suppose that the problem is caused by some search scope or environment problem. However, I am really a newbie to environments in R.
How can I get my check_input-function work without the need to define the test_if function inside it?
Many thanks, Silke
Here is my minimal working example:
library(assertthat)
test_if <- function(...,msg=NULL) {
test <- validate_that(...,msg=msg)
if (is.logical(test)) {
return(list(assertation=test,msg=NULL))
}
if (is.character(test)) {
return(list(assertation=FALSE,msg=test))
}
}
test_if(2==3)
test_if(3==3)
test_if(2==3,3==4,msg="something is wrong")
### To check different inputs
check_input1 <- function(value1 = NULL,value2 = NULL) {
test_if <- function(...,msg=NULL) {
test <- validate_that(...,msg=msg)
if (is.logical(test)) {
return(list(assertation=test,msg=NULL))
}
if (is.character(test)) {
return(list(assertation=FALSE,msg=test))
}
}
error_msg <- ""
error_status <- FALSE
check <- test_if(is.numeric(value1))
error_msg <- check$msg
error_status <- check$assertation
return(list(error_msg=error_msg,error_status=error_status))
}
check_input2 <- function(value1 = NULL,value2 = NULL) {
error_msg <- ""
error_status <- FALSE
check <- test_if(is.numeric(value1))
error_msg <- check$msg
error_status <- check$assertation
return(list(error_msg=error_msg,error_status=error_status))
}
check_input1(value1=1)
check_input2(value1=1)

Advanced error handling

I recently posed this question and thankfully was pointed to withRestarts() which seems pretty awesome and powerful to me :-) Now I'm eager to understand R's error handling capabilities in a bit more detail.
Actual questions
What is the recommended usage of simpleCondition()? Never used it before, but I thought it might be useful for designing custom errors and warnings that are in fact "true" conditions. Could it be used to build a database of specific conditions for which specific handlers are available?
Is there a way to "freeze" a certain state of the entire R workspace and return to it to restart a computation at a certain point? I'm aware of save.image(), but AFAIU, this doesn't store the "state" of the search path (search() or searchpaths()).
For those interested
Two code examples
illustration of my current use of withRestarts in dependence on this blog post
attempt to define a "custom condition"
I'd appreciate any comments/suggestion on what to do better ;-)
Example 1
require("forecast")
autoArimaFailsafe <- function(
x,
warning=function(w, ...) {
message("autoArimaFailsafe> warning:")
message(w)
invokeRestart("donothing")},
error=function(e, ...) {
message("autoArimaFailsafe> error:")
message(e)
invokeRestart("abort")}
) {
withRestarts(
out <- tryCatch(
{
expr <- expression(auto.arima(x=x))
return(eval(expr))
},
warning=warning,
error=error
),
donothing=function(...) {
return(eval(expr))
},
abort=function(...) {
message("aborting")
return(NULL)
}
)
}
data(AirPassengers)
autoArimaFailsafe(x=AirPassengers)
autoArimaFailsafe(x="a")
Example 2
require("forecast")
autoArimaFailsafe <- function(
x,
warning=function(w, ...) {
message("autoArimaFailsafe> warning")
invokeRestart("donothing")},
error=function(e, ...) {
message("autoArimaFailsafe> error")
invokeRestart("abort")},
condition=function(cond, ...) {
out <- NULL
message(cond)
condmsg <- conditionMessage(c=cond)
condclass <- class(cond)
if (any(class(cond) == "simpleWarning")) {
out <- warning(w=cond)
} else if (any(class(cond) == "simpleError")) {
out <- error(e=cond)
} else if (any(class(cond) == "simpleCondition")) {
if (condmsg == "invalid class: character") {
out <- invokeRestart("forcedefault")
}
}
return(out)
}
) {
withRestarts(
out <- tryCatch(
{
expr <- expression(auto.arima(x=x))
if (class(x) == "character") {
expr <- signalCondition(
simpleCondition("invalid class: character",
call=as.call(expr))
)
}
return(eval(expr))
},
condition=condition
),
donothing=function(...) {return(eval(expr))},
abort=function(...) {
message("aborting")
return(NULL)
},
forcedefault=function(...) {
data(AirPassengers)
expr <- expression(auto.arima(x=AirPassengers))
return(eval(expr))
}
)
}
autoArimaFailsafe(x=AirPassengers)
autoArimaFailsafe(x=NULL)
autoArimaFailsafe(x="a")
This post references the inspiration for R's condition handling.
For 1., I think of simpleCondition as illustrating how one can construct custom conditions, e.g,.
myCondition <-
function(message, call=NULL, type=c("overflow", "underflow", "zero"))
{
type <- match.arg(type) # only allowed types past here
class <- c(type, "my", "condition")
structure(list(message = as.character(message), call = call),
class = class)
}
is a constructor for making custom conditions
> myCondition("oops")
<overflow: oops>
> myCondition("oops", type="underflow")
<underflow: oops>
These conditions can be used in tryCatch or withCallingHandlers
xx <- tryCatch({
signalCondition(myCondition("oops", type="underflow"))
}, underflow=function(e) {
message("underflow: ", conditionMessage(e))
NA # return value, assigned to xx
})
These are S3 classes so can have a linear hierarchy -- bad and worse are both subclasses of error.
myError <-
function(message, call=NULL, type=c("bad", "worse"))
{
type <- match.arg(type)
class <- c(type, "error", "condition")
structure(list(message=as.character(message), call=call),
class=class)
}
One might also create an error that extends the 'simpleError' S3 class as cond <- simpleError("oops"); class(cond) = c("myerr", class(cond)
With tryCatch we just get access to a single handler, the first (in the sense described on ?tryCatch) to match the class of condition
tryCatch({
stop(myError("oops", type="worse"))
}, bad = function(e) {
message("bad error: ", conditionMessage(e))
}, worse = function(e) {
message("worse error: ", conditionMessage(e)) # here's where we end up
}, error=function(e) {
message("error: ", conditionMessage(e))
})
With withCallingHandlers we have the opportunity to hit multiple handlers, provided we don't invoke a restart
withCallingHandlers({
stop(myError("oops", type="bad"))
}, bad = function(e) { # here...
message("bad error: ", conditionMessage(e))
}, worse = function(e) {
message("worse error: ", conditionMessage(e))
}, error=function(e) { # ...and here...
message("error: ", conditionMessage(e))
}) # ...and top-level 'error'
withCallingHandlers({
x <- 1
warning(myError("oops", type="bad"))
"OK"
}, bad = function(e) { # here, but continue at the restart
message("bad warning: ", conditionMessage(e))
invokeRestart("muffleWarning")
}, worse = function(e) {
message("worse warning: ", conditionMessage(e))
})
I'm not so sure about your question 2; I think this is the situation that calling handlers are designed to address -- the entire frame where the condition was invoked is poised waiting to continue, once you invoke the restart.

How to patch an S4 method in an R package?

If you find a bug in a package, it's usually possible to patch the problem with fixInNamespace, e.g. fixInNamespace("mean.default", "base").
For S4 methods, I'm not sure how to do it though. The method I'm looking at is in the gWidgetstcltk package. You can see the source code with
getMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"))
I can't find the methods with fixInNamespace.
fixInNamespace(".svalue", "gWidgetstcltk")
Error in get(subx, envir = ns, inherits = FALSE) :
object '.svalue' not found
I thought setMethod might do the trick, but
setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"),
definition = function (obj, toolkit, index = NULL, drop = NULL, ...)
{
widget = getWidget(obj)
sel <- unlist(strsplit(tclvalue(tcl(widget, "selection")),
" "))
if (length(sel) == 0) {
return(NA)
}
theChildren <- .allChildren(widget)
indices <- sapply(sel, function(i) match(i, theChildren))
inds <- which(visible(obj))[indices]
if (!is.null(index) && index == TRUE) {
return(inds)
}
if (missing(drop) || is.null(drop))
drop = TRUE
chosencol <- tag(obj, "chosencol")
if (drop)
return(obj[inds, chosencol, drop = drop])
else return(obj[inds, ])
},
where = "package:gWidgetstcltk"
)
Error in setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"), :
the environment "gWidgetstcltk" is locked; cannot assign methods for function ".svalue"
Any ideas?
How about the old-school way of getting the source, applying the change and rebuilding?
you can first get the generic out, and then fix the generic by setMethod in your global environment, and then assign it back to that namespace
.svalue <- gWidgetstcltk:::.svalue
setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"),
definition = function (obj, toolkit, index = NULL, drop = NULL, ...)
{
widget = getWidget(obj)
sel <- unlist(strsplit(tclvalue(tcl(widget, "selection")),
" "))
if (length(sel) == 0) {
return(NA)
}
theChildren <- .allChildren(widget)
indices <- sapply(sel, function(i) match(i, theChildren))
inds <- which(visible(obj))[indices]
if (!is.null(index) && index == TRUE) {
return(inds)
}
if (missing(drop) || is.null(drop))
drop = TRUE
chosencol <- tag(obj, "chosencol")
if (drop)
return(obj[inds, chosencol, drop = drop])
else return(obj[inds, ])
}#,
#where = "package:gWidgetstcltk"
)
assignInNamespace(".svalue", .svalue, ns = "gWidgetstcltk")

Resources