Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get variables in error messages?

Here is my code:

test <- function(y){
irisname <- c("Sepal.Length","Sepal.Width","Petal.Length","Petal.Width","Species")
  if(y %in% irisname){
    print(y)
  } else{
    test <- function(...) stop("dummy error")
    test(y)
  }
}
> test("ds")
  Error in test(y) : dummy error 

In the result: "Error in test(y) : dummy error ", I need "ds" in test("ds"), not test(y).

How can I do that?

like image 542
John Avatar asked Dec 26 '22 08:12

John


2 Answers

This almost does it (there's an extra colon ...), by using call.=FALSE to suppress the information about the call and hacking it into the error message.

update: added quotation marks to error #1; explained a bit more about why this problem is hard.

I don't know the structure of your code, but you are making life considerably harder for yourself by passing objects farther down into the structure. It would be a lot easier to call stop() directly from within your first level, or to use the information carried in y directly within your error message.

test <- function(y,stop=FALSE){
   irisname <- c("Sepal.Length","Sepal.Width",
       "Petal.Length","Petal.Width","Species")
   if (stop) stop(sprintf("premature stop: var %s",y))
   if(y %in% irisname){
       print(y)
   } else{
     test <- function(...) {
         stop(sprintf("in test(\"%s\"): dummy error",...),
               call.=FALSE)
     }
     test(y)
 }
}

test("junk")
## Error: in test("junk"): dummy error
test("junk",stop=TRUE)
## Error in test("junk", stop = TRUE) : premature stop: var junk

Getting rid of the spurious first colon in the output of test("junk") will be considerably harder, because the Error: string is hard-coded within R. Your best bet is probably, somehow, to print your own custom error message and then stop silently, or recreate the behaviour of stop() without generating the message (see ?condition: e.g. return(invisible(simpleError("foo")))). However, you're going to have to jump through a lot of hoops to do this, and it will be hard to ensure that you get exactly the same behaviour that you would have with stop() (e.g. will the error message have been saved in the error-message buffer?)

What you want to do is probably possible by mucking around with R internals enough, but in my opinion so hard that it would be better to rethink the problem ...

Good luck.

like image 70
Ben Bolker Avatar answered Jan 11 '23 10:01

Ben Bolker


You could check the argument right at the start of the function. match.arg might come in handy, or you could print custom message and return NA.

two updates below

> test <- function(y)
  {
    if(!(y %in% names(iris))){
      message(sprintf('test("%s") is an error. "%s" not found in string', y, y))
      return(NA) ## stop all executions and exit the function
    }
    return(y)  ## ... continue
  }

> test("Sepal.Length")
# [1] "Sepal.Length"
> test("ds")
# test("ds") is an error. "ds" not found in string
# [1] NA

Add/Edit : Is there a reason why you're nesting a function when the function goes to else? I removed it, and now get the following. It seems all you are doing is checking an argument, and end-users (and RAM) want to know immediately if they enter an incorrect default arguments. Otherwise, you're calling up unnecessary jobs and using memory when you don't need to.

test <- function(y){
     irisname <- c("Sepal.Length","Sepal.Width","Petal.Length","Petal.Width","Species")
     if(y %in% irisname){
         print(y)
     } else{
         stop("dummy error")
     }
 }
> test("ds")
# Error in test("ds") : dummy error
> test("Sepal.Length")
# [1] "Sepal.Length"

You could also use pmatch, rather than match.arg, since match.arg prints a default error.

> test2 <- function(x)
  {
      y <- pmatch(x, names(iris))
      if(is.na(y)) stop('dummy error')
      names(iris)[y]
  }
> test2("ds")
# Error in test2("ds") : dummy error
> test2("Sepal.Length")
# [1] "Sepal.Length"
like image 23
Rich Scriven Avatar answered Jan 11 '23 10:01

Rich Scriven