Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to catch warnings sent during S4 method selection

Tags:

r

warnings

s4

Warnings generated when evaluation arguments of S4 generic functions can not be caught using withCallingHandlers().

Illustration of the normal behaviour of withCallingHandlers:

### simple function that sends a warning
send_warning <- function() {
  warning('send_warning')
}
send_warning()
# Warning message:
# In send_warning() : send_warning

### the warning can be caught by withCallingHandlers
withCallingHandlers(send_warning(), warning = function(w) 
  { stop('got warning:', w) })
# Error in (function (w)  : 
#   got warning:simpleWarning in send_warning(): send_warning

Now, let's make the warning be emitted during a S4 dispatch:

### simplest method ever
setGeneric('my_method', function(x) standardGeneric('my_method') )
setMethod('my_method', 'ANY', function(x) str(x) )

### call it with an argument that produces a warning: seems to work
my_method(warning('argh'))
#  chr "argh"
# Warning message:
# argh 

### !!! BUT it is not caught !!!!!!!!!!!!!!!!!!:
withCallingHandlers(my_method(warning('argh')), warning = function(w) 
  { stop('got warning:', w) })
#  chr "argh"
# Warning message:
# argh 

A last example to show that this does not happen during S4 method call:

setGeneric('my_method2', function(x) standardGeneric('my_method2') )
setMethod('my_method2', 'ANY', function(x) warning('my_method2') )
my_method2()
# Warning message:
# In my_method2() : my_method2

### warning is caught
withCallingHandlers(my_method2(), warning = function(w) 
  { stop('got warning:', w) })
# Error in (function (w)  : 
#   got warning:simpleWarning in my_method2(): my_method2

From what I understand, there seems to be a special behavior of the warnings emitted during the S4 dispatch. I'd like to know why and how to catch them.

like image 796
Karl Forner Avatar asked Nov 28 '13 13:11

Karl Forner


1 Answers

My original guess was that this is because method dispatch muffles warnings

f = function(x) withCallingHandlers(x, warning=function(w) { 
    cat('muffled\n')
    invokeRestart("muffleWarning")
})

and then

> withCallingHandlers(f(warning("f")), warning=function(w) message("caught"))
muffled

Digging deeper, the actual warning call is in this C stack trace

#0  do_warning (call=0x3d4ad50, op=0x9c4bf0, args=0x45b9968, rho=0x45b9c40) at /home/mtmorgan/src/R-devel/src/main/errors.c:1140
#1  0x00000000004b4198 in bcEval (body=<optimized out>, rho=<optimized out>, useCache=<optimized out>) at /home/mtmorgan/src/R-devel/src/main/eval.c:4700
#2  0x00000000004bff40 in Rf_eval (e=0x3d45618, rho=0x45b9c40) at /home/mtmorgan/src/R-devel/src/main/eval.c:554
#3  0x00000000004c4e4d in Rf_applyClosure (call=0x45b8630, op=0x3d45730, arglist=<optimized out>, rho=0x9e7638, suppliedenv=0x9e7670) at /home/mtmorgan/src/R-devel/src/main/eval.c:1033
#4  0x00000000004c0005 in Rf_eval (e=0x45b8630, rho=0x9e7638) at /home/mtmorgan/src/R-devel/src/main/eval.c:670
#5  0x00000000004c0695 in forcePromise (e=0x45b9410) at /home/mtmorgan/src/R-devel/src/main/eval.c:458
#6  0x00000000004c0462 in Rf_eval (e=0xa1d338, rho=0x45b9368) at /home/mtmorgan/src/R-devel/src/main/eval.c:577
#7  0x000000000046fbfb in protectedEval (d=0x7fffffffc7f0) at /home/mtmorgan/src/R-devel/src/main/context.c:750
#8  0x0000000000470d48 in R_ToplevelExec (fun=0x46fbe0 <protectedEval>, data=0x7fffffffc7f0) at /home/mtmorgan/src/R-devel/src/main/context.c:705
#9  0x0000000000470de7 in R_tryEval (e=<optimized out>, env=<optimized out>, ErrorOccurred=0x7fffffffc89c) at /home/mtmorgan/src/R-devel/src/main/context.c:764
#10 0x0000000000470e26 in R_tryEvalSilent (e=<optimized out>, env=<optimized out>, ErrorOccurred=<optimized out>) at /home/mtmorgan/src/R-devel/src/main/context.c:787
#11 0x00007ffff49230b9 in R_dispatchGeneric (fname=0x44b37e8, ev=0x45b9368, fdef=0x45b92f8) at /home/mtmorgan/src/R-devel/src/library/methods/src/methods_list_dispatch.c:993
#12 0x00000000004f5337 in do_standardGeneric (call=<optimized out>, op=<optimized out>, args=<optimized out>, env=0x45b9368) at /home/mtmorgan/src/R-devel/src/main/objects.c:1167
....

where in the C code of R_dispatchGeneric method dispatch is trying to figure out the class of the argument by evaluating it

989         if(arg_sym == R_dots) {
990         thisClass = dots_class(ev, &check_err);
991         }
992         else {
993         PROTECT(arg = R_tryEvalSilent(arg_sym, ev, &check_err));
994         if(!check_err)
995             thisClass = R_data_class(arg, TRUE);
996         UNPROTECT(1); /* for arg */
997         }

This seems to be done to implement a C-level error handler:

998         if(check_err)
999         error(_("error in evaluating the argument '%s' in selecting a method for function '%s': %s"),
1000                  CHAR(PRINTNAME(arg_sym)),CHAR(asChar(fname)),
1001                  R_curErrorBuf());

Rf_tryEvalSilent evaluates at the top level, something like at the command prompt where there are no calling handlers established; you can see this in the C code, where the handler stack is set to NULL

686 Rboolean R_ToplevelExec(void (*fun)(void *), void *data)
687 {
688     RCNTXT thiscontext;
689     RCNTXT * volatile saveToplevelContext;
690     volatile SEXP topExp, oldHStack;
691     Rboolean result;
692 
693 
694     PROTECT(topExp = R_CurrentExpr);
695     PROTECT(oldHStack = R_HandlerStack);
696     R_HandlerStack = R_NilValue;
697     saveToplevelContext = R_ToplevelContext;

Since argument evaluation starts without handlers, a work-around might be

my_method(withCallingHandlers(warning('arrgh'), warning=function(w) ...))

but that's probably not practical.

like image 146
Martin Morgan Avatar answered Oct 31 '22 02:10

Martin Morgan