How to catch warnings sent when choosing method S4

Warnings generated when evaluative arguments to common S4 functions cannot be detected using CallingHandlers ().

Illustration of normal behavior 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 a warning issued during S4 submission:

 ### 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 

The last example showing that this does not happen when calling the S4 method:

 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 for alerts issued during S4 submission. I would like to know why and how to catch them.

+7
r warnings s4
source share
1 answer

My initial hunch was that this is because sending a method drowns out 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 alert 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 R_dispatchGeneric method, the dispatch method tries 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 is evaluated at the top level, something like a command line where there are no call handlers; you can see this in 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 begins without handlers, a workaround may be

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

but this is probably not practical.

+3
source share

All Articles