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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With