Skip to content

How to catch both warnings and errors?

6 messages · David Winsemius, Marius Hofert, Martin Maechler

#
Dear expeRts,

I am struggling with warning/error handling. 

I would like to call a function which can produce either 
a) normal output
b) a warning
c) an error

Since the function is called several (thousand) times in a loop, I would like 
to proceed "quietly" and collect the warnings and errors [to deal with them at a
later point]. 

I have seen constructs with tryCatch (which can deal with errors) and with
withCallingHandlers (which can deal with warnings), but I cannot figure out how
to catch *both* warnings and errors. Below is a minimal example of the function 
that is called several times in a large loop. The function should catch warnings 
and errors; the former work fine, but with the latter I do not know how to proceed.

The function should *always* return the list with the three components.

How can I achieve this?

Cheers,

Marius

Ps: How can I get the warning/error message in a nice string (as it is printed
in a normal warning/error message)? The approach shown below is somehow ugly. 
Basically, I had trouble converting the call w$call to a string.

## based on http://tolstoy.newcastle.edu.au/R/help/04/06/0217.html
## and http://www.mail-archive.com/r-help at r-project.org/msg81380.html
f <- function(x){
    ## warnings
    w.list <- NULL # init warning
    w.handler <- function(w){ # warning handler
	warn <- simpleWarning(w$message, w$call) # build warning
        w.list <<- c(w.list, paste(warn, collapse = " ")) # save warning
        invokeRestart("muffleWarning")
    }
    ## errors
    e.list <- NULL # init errors
    e.handler <- function(e){ # error handler
	err <- simpleError(e$message, e$call)
	e.list <<- c(e.list, paste(err, collapse = " ")) # save error 
    }
    ## execute command   
    res <- withCallingHandlers(log(x), warning = w.handler, error = e.handler)
    ## return result with warnings and errors
    list(result = res, warning = w.list, error = e.list)
}

f(1) 
f(-1) 
f("a")
#
On Dec 5, 2010, at 3:13 PM, Marius Hofert wrote:

            
I do not see the function warnings() being used below:

?warnings

It delivers the stored warnings with different default behavior for  
interactive and non-interactive sessions.
Made some changes in you code but don't know if it is what you were  
hoping for:

f <- function(x){
    ## warnings
    w.list <- NULL # init warning
    w.handler <- function(w){ # warning handler
	    warn <- simpleWarning(w$message, w$call) # build warning
# first change here
        w.list <<- c(w.list, paste(warnings(), collapse = " ")) # save  
warning
        invokeRestart("muffleWarning")
        }
    ## errors
      e.list <- NULL # init errors   # not sure this is  good idea
      e.handler <- function(e){ # error handler
	     err <- c(e.list, e$message, e$call) # save error
	     return( err)
         }
    ## execute command
# wrapped cal in try()
    res <- withCallingHandlers(try(log(x)), warning = w.handler, error  
= e.handler)
    ## return result with warnings and errors
    list(result = res, warning = w.list, error = e.list)
}
David Winsemius, MD
West Hartford, CT
#
On 2010-12-06, at 01:07 , David Winsemius wrote:

            
Dear David,

many thanks for your help. 
If I call your code with f(-1) and f("a"), I obtain:
$result
[1] NaN

$warning
[1] ""

$error
NULL

=> The problem is that the warning is not given.
Error in log(x) : Non-numeric argument to mathematical function
$result
[1] "Error in log(x) : Non-numeric argument to mathematical function\n"
attr(,"class")
[1] "try-error"

$warning
NULL

$error
NULL

=> The problem is that the error message is printed to the R console instead of suppressed (setting silent = TRUE didn't help either). Further, the $error component is empty (the error message should appear there -- if possible)

Do you know a solution?

Cheers,

Marius
#
On Dec 5, 2010, at 7:35 PM, Marius Hofert wrote:

            
Sorry. I was being misled by warnings that existed at my global level  
into thinking i had succeeded. This modification will suppress warning  
and error but will not populate the lists as we had hoped:

f <- function(expr){
  ## warnings
  w.list <- NULL # init warning
  w.handler <- function(w){ # warning handler
	    warn <- c(w$message, w$call) # build warning
# first change here
      muffleWarning <<- c(w.list, warn, collapse = " ") # save warning
           invokeRestart("muffleWarning")}
  ## errors
    e.list <- NULL # init errors   # not sure this is  good idea
    e.handler <- function(e){ # error handler
	     e.list <<- c(e.list, e$message, e$call) # save error
	     return( e.list)
       }
  ## execute command
# wrapped cal in try()
  res <- withCallingHandlers(try(expr, silent=TRUE), warning =  
w.handler, error = e.handler)
  ## return result with warnings and errors
   list(result = res, warning = w.list, error = e.list)
}

 > test <- f(log(-1))
 > test
$result
[1] NaN

$warning
NULL

$error
NULL

 > test <- f(log("a"))
 > test
$result
[1] "Error in log(\"a\") : Non-numeric argument to mathematical  
function\n"
attr(,"class")
[1] "try-error"

$warning
NULL

$error
NULL
David Winsemius, MD
West Hartford, CT
#
Hmm... still not quite what I was hoping for... but thanks anyway. 

I would like to have the output in the following form:
$result
[1] 0

$warning
[1] NULL # or ""

$error
[1] NULL # or ""
$result
[1] NaN

$warning
[1] "Warning in log(-1) : NaNs produced" # or something similar

$error
[1] NULL # or ""
$result
[1] NULL # or NA 

$warning
[1] NULL # or ""

$error
[1] "Error in log("a") : Non-numeric argument to mathematical function"

Has anyone done that before? I think it's quite a natural problem although I could not find a solution online (dealing with *both* warnings *and* errors). 

Cheers,

Marius
3 days later
#
> Hmm... still not quite what I was hoping for... 
indeed.

We (Marius and I) now sat together,
and have constructed the following quite nice,
quite general solution:

With thanks to Luke Tierney's explanation (from 2004 !)
     http://tolstoy.newcastle.edu.au/R/help/04/06/0217.html
and Bill Dunlap's note (mentioned in Marius' original post):

##' We want to catch *and* save both errors and warnings, and in the case of
##' a warning, also keep the computed result.
##'
##' @title tryCatch both warnings and errors
##' @param expr
##' @return a list with 'value' and 'warning', where 
##'  'value' may be an error caught.
##' @author Martin Maechler
tryCatch.W.E <- function(expr)
{
    W <- NULL
    w.handler <- function(w){ # warning handler
	W <<- w
	invokeRestart("muffleWarning")
    }
    list(value = withCallingHandlers(tryCatch(expr, error = function(e) e),
				     warning = w.handler),
	 warning = W)
}


Testing it here for Marius' three cases :
List of 2
 $ value  : num 0.693
 $ warning: NULL
List of 2
 $ value  : num NaN
 $ warning:List of 2
  ..$ message: chr "NaNs wurden erzeugt"
  ..$ call   : language log(-1)
  ..- attr(*, "class")= chr [1:3] "simpleWarning" "warning" "condition"
List of 2
 $ value  :List of 2
  ..$ message: chr "Nicht-numerisches Argument f?r mathematische Funktion"
  ..$ call   : language log("a")
  ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
 $ warning: NULL

---
Martin Maechler, ETH Zurich