Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error handling in R: access known object within function at time of function error

here's a example function, let's say i'm unable able to edit it.

myfun <- function(){ x <- 1 ; stop( "error here" ) }

when the stop() occurs, how would i access the object x to see what it was?

not sure if i should use something like this, or if i need something related to dump.frames, or if this is simply not possible without changing myfun()

withCallingHandlers( myfun() , error = function(e){ print( e ) } ) 

i am looking for something similar to this behavior, but need to do it without myfun()

myfun <- function(){ on.exit( print( x ) ) ; x <- 1 ; stop( "error here" ) }
myfun()

for my use-case, i am unable to change myfun() because there are about fifty functions, but i want to know a consistently-named object within each of them at time of error

thanks!

like image 918
Anthony Damico Avatar asked Nov 20 '17 08:11

Anthony Damico


2 Answers

Option 1:

You could, of course, step through the functions using browser(), but this may be too time consuming for what you want.

Option 2

Although you say that the reason you are unable to change myfun() is "because there are about fifty functions", this does not present an insurmountable barrier. One of the powerful things about R is that it can edit its own functions. So we can add an on.exit call to the function like this:

body(myfun) <- as.call(c(as.name("{"), expression(on.exit(print(x))), body(myfun)))

We can wrap this in a handy function which does not change the original function like this:

print.on.exit = function(f, ...){
  body(f) <- as.call(c(as.name("{"), expression(on.exit(print(x))), body(f)))
  f(...)
}

print.on.exit(myfun)
like image 99
dww Avatar answered Nov 14 '22 22:11

dww


Here's a way to do it with trace. I use also purr::walk as it is silent but you can use sapply instead for the same effect.

First let's define 3 functions, as you have 50 of them:

myfun1 <- function(){ x <- 1 ; stop( "error here" ) }
myfun2 <- function(){ x <- 2 ; banana(2) }
myfun3 <- function(){ x <- 3 ; x <- "potatoe" + x }

myfun1() # Error in myfun1() : error here
myfun2() # Error in myfun2() : could not find function "banana"
myfun3() # Error in "potatoe" + x : non-numeric argument to binary operator

Then put their names in a vector, and just apply this code, that will print x whenever exiting the function :

funs <- c("myfun1","myfun2","myfun3")
purrr::walk(funs,trace,exit = quote(print( x )))

myfun1() 
# Error in myfun1() : error here
# Tracing myfun1() on exit 
# [1] 1

myfun2()
# Error in myfun2() : could not find function "banana"
# Tracing myfun2() on exit 
# [1] 2

myfun3()
# Error in "potatoe" + x : non-numeric argument to binary operator
# Tracing myfun3() on exit 
# [1] 3

And then get back to normal:

purrr::walk(funs,untrace)

EDIT

With method above, x will be printed even when there's no error

myfun4 <- function(){ x <- 4 ; TRUE }
trace(myfun4, exit = quote(print( x )))
myfun4()
Tracing myfun4() on exit 
[1] 4
[1] TRUE

To print x only on error, you can use following code, by the same token I've set print parameter to FALSE for lighter display and made it clearer than the printed value in case of error was x :

untrace(myfun4)
funs <- c("myfun1","myfun2","myfun3","myfun4") 
purrr::walk(funs,trace, print=FALSE,exit=quote(
  if(exists(".Traceback") && .Traceback[[length(.Traceback)]] == sys.calls()[[1]]){
    message("x at time of error:")
    print(x)}))

myfun3()
# Error in "potatoe" + x : non-numeric argument to binary operator
# x at time of error:
# [1] 3

myfun4()
# [1] TRUE
like image 42
Moody_Mudskipper Avatar answered Nov 14 '22 21:11

Moody_Mudskipper