Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

warning.expression prevents last.warning from update

Tags:

r

I'm interested in trying to manipulate warnings on call without creating support infrastructure around a method. That is, I need to be able to catch the warning without resorting to wrapping code with:

tryCatch(..., warning = function() { action() } )

I believe I can do this using the warning.expression handler.

However, the issue I am running into is the last.warning when invoked during the warning dispatch of warning.expression fails to retrieve the latest warning.

For example:

warning_handler = function() {
  if (exists("last.warning", baseenv()) &&
      !is.null(last.warning)) {
    warning_contents =  names(last.warning)
  } else {
    warning_contents =  NA
  }

  message(warning_contents)
}


options(warning.expression = quote({ warning_handler() }))

warning("test1")
# test1
warning("testing2")
# test1
warning("sampletest3")
# test1

If I revert to using the default handler, e.g. NULL, then the message is updated at the cost of not being able to interact with it.

options(warning.expression = NULL)

warning("test1")
# Warning message:
# test1
warning("testing2")
# Warning message:
# testing2
warning("sampletest3")
# Warning message:
# sampletest3

Am I missing something related to the handler or?

like image 219
coatless Avatar asked Oct 15 '25 08:10

coatless


2 Answers

Adding a warning.expression almost certainly interferes with the warning collection mechanism. I'm not 100% certain about this, but if you look at (R.3.4.0, I have an old copy of the sources), you can see at errors.c@335:

static void vwarningcall_dflt(SEXP call, const char *format, va_list ap)
{
    int w;
    SEXP names, s;
    const char *dcall;
    char buf[BUFSIZE];
    RCNTXT *cptr;
    RCNTXT cntxt;

    if (inWarning)
        return;

    s = GetOption1(install("warning.expression"));
    if( s != R_NilValue ) {
        if( !isLanguage(s) &&  ! isExpression(s) )
            error(_("invalid option \"warning.expression\""));
        cptr = R_GlobalContext;
        while ( !(cptr->callflag & CTXT_FUNCTION) && cptr->callflag )
            cptr = cptr->nextcontext;
        eval(s, cptr->cloenv);
        return;
    }
  // ... snip ...


    else if(w == 0) {   /* collect them */
        if(!R_CollectWarnings) setupwarnings();
        if(R_CollectWarnings < R_nwarnings) {
            SET_VECTOR_ELT(R_Warnings, R_CollectWarnings, call);
            Rvsnprintf(buf, min(BUFSIZE, R_WarnLength+1), format, ap);
  // ... snip ...
}

So the part after the return doesn't run.

And it looks like last.value is populated by printWarnings in errors.c@466, so this either happens after the warning.expressions handler, or more likely, not at all:

attribute_hidden
void PrintWarnings(void)
{
  // ... snip to very end of fun ...

    /* now truncate and install last.warning */
    PROTECT(s = allocVector(VECSXP, R_CollectWarnings));
    PROTECT(t = allocVector(STRSXP, R_CollectWarnings));
    names = CAR(ATTRIB(R_Warnings));
    for(i = 0; i < R_CollectWarnings; i++) {
        SET_VECTOR_ELT(s, i, VECTOR_ELT(R_Warnings, i));
        SET_STRING_ELT(t, i, STRING_ELT(names, i));
    }
    setAttrib(s, R_NamesSymbol, t);
    SET_SYMVALUE(install("last.warning"), s);
    UNPROTECT(2);

    endcontext(&cntxt);

    inPrintWarnings = 0;
    R_CollectWarnings = 0;
    R_Warnings = R_NilValue;
    return;
}

So it is likely that's what going on. I haven't worked through the flow patterns so it's possible I'm wrong. If I'm right it seems like there isn't a way to get the warning info from warning.expression.

Beyond that, note that last.warning is documented as being undocumented, FWIW.

like image 171
BrodieG Avatar answered Oct 17 '25 22:10

BrodieG


Another answer courtesy of @lionel in chat was: to install a warning handler at top level, which you can do by calling into internal functions with .Internal()

handlers <- list(warning = function(cnd) cat("hello\n")) 
classes <- names(handlers) 
.Internal(.addCondHands(classes, handlers, globalenv(), NULL, TRUE)) 

warn("plop")

The only downside to this approach is CRAN would reject a package on submission due to the internal call.

like image 27
coatless Avatar answered Oct 17 '25 22:10

coatless



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!