Search code examples
rwarnings

Compress any numbers of warning messages into one


From ?options, the description of warn said

If 10 or fewer warnings were signalled, they will be printed, otherwise a message saying how many were signalled.

So if there are more than 10 warnings, they will be compressed into 1 message. For example:

f <- function() {
  warning("warn 1")
  warning("warn 2")
  warning("warn 3")
  warning("warn 4")
  warning("warn 5")
  warning("warn 6")
  warning("warn 7")
  warning("warn 8")
  warning("warn 9")
  warning("warn 10")
  warning("warn 11")
  return(1)
}

f()

[1] 1
There were 11 warnings (use warnings() to see them)

How can I reduce the limit of 10 to 1? I.e. Even if I have only one warning, I expect it to show

There were 1 warnings (use warnings() to see them)


Solution

  • Couldn't find an option to globally set this threshold to a custom value (might be a nice feature request you could discuss on r-devel). Instead, you could use a wrapper function to summarize warnings where needed. We'd need an environment since---for good reason---'last.warning' is read-only.

    W <- \(FUN) {
      env <- new.env()
      env$w_lst <- list()  ## empty warning list to increment
      res <- withCallingHandlers(FUN, warning=\(w) {
        env$w_lst <- c(env$w_lst, list(w))
        invokeRestart('muffleWarning')
      })
      n <- length(env$w_lst)  ## count warnings
      if (n > 0) {
        more <- (n > 1L) + 1L
        msg <- sprintf('There %s %s warning%s (use warnings2() to see %s)',
                       switch(more, 'was', 'were'),
                       n,
                       switch(more, '', 's'),
                       switch(more, 'it', 'them')
        )
        message('Warning: ', msg)
        assign('last.warning', env$w_lst, envir=warn_env)  ## assign warnings to env
      }
      res
    }
    ## alternative warnings function
    warnings2 <- \() {
      if (exists("last.warning", envir=warn_env)) {
        w_lst <- get("last.warning", envir=warn_env)
        for (i in seq_along(w_lst)) {
          message(sprintf('Warning %d: %s', i, conditionMessage(w_lst[[i]])))
        }
      } else {
        base::warnings()
      }
    }
    

    > warn_env <- new.env()  ## create env for 'last.waning'
    > 
    > W(f(0))
    [1] 1
    > W(f(1))
    Warning: There was 1 warning (use warnings2() to see it)
    [1] 1
    > W(f(2))
    Warning: There were 2 warnings (use warnings2() to see them)
    [1] 1
    > W(f(11))
    Warning: There were 11 warnings (use warnings2() to see them)
    [1] 1
    > 
    > warnings2()
    Warning 1: foo
    Warning 2: foo
    Warning 3: foo
    Warning 4: foo
    Warning 5: foo
    Warning 6: foo
    Warning 7: foo
    Warning 8: foo
    Warning 9: foo
    Warning 10: foo
    Warning 11: foo
    

    Data:

    f <- \(n) {
      replicate(n, warning('foo'))
      return(1)
    }