Как перехватить предупреждения, отправленные при выборе метода S4
Предупреждения, генерируемые при оценке аргументов универсальных функций S4, не могут быть перехвачены с помощью withCallingHandlers ().
Иллюстрация нормального поведения 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
Теперь давайте отправим предупреждение во время отправки S4:
### 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
Последний пример, чтобы показать, что это не происходит во время вызова метода S4:
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
Из того, что я понимаю, кажется, что есть особенное поведение предупреждений, отправленных во время отправки S4. Я хотел бы знать, почему и как их поймать.
1 ответ
Моя первоначальная догадка заключалась в том, что метод диспетчеризации приглушает предупреждения.
f = function(x) withCallingHandlers(x, warning=function(w) {
cat('muffled\n')
invokeRestart("muffleWarning")
})
а потом
> withCallingHandlers(f(warning("f")), warning=function(w) message("caught"))
muffled
Копая глубже, фактический вызов предупреждения находится в этой трассировке стека C
#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
....
где в коде C R_dispatchGeneric
Метод dispatch пытается выяснить класс аргумента, оценивая его
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 }
Похоже, это сделано для реализации обработчика ошибок уровня C:
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
оценивает на верхнем уровне, что-то вроде командной строки, где не установлены обработчики вызовов; Вы можете увидеть это в коде C, где стек обработчика установлен в 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;
Поскольку оценка аргумента начинается без обработчиков, обходной путь может быть
my_method(withCallingHandlers(warning('arrgh'), warning=function(w) ...))
но это, вероятно, не практично.