【问题标题】:How to display a warning only once per session?如何在每个会话中仅显示一次警告?
【发布时间】:2020-04-07 08:26:42
【问题描述】:

我的包中有一个功能需要谨慎使用。

用户应该意识到这一点,但如果他/她认为情况正常,那么每次调用函数时都显示警告会很麻烦。

我经常看到只显示一次的警告。它们调试起来非常痛苦,所以我找不到可重现的示例(如果有的话,我会添加一个),但它们会显示特定的警告消息,然后是 rlang 信息:

此警告在每个会话中显示一次

有很多帮助想要调试这些消息(例如 hereherehere,只需 google “r 此警告在每个会话中显示一次”)

我认为lifecyle 包经常使用这些包进行软弃用,但我无法发现lifecycle:::lifecycle_build_message 中的技巧。

如何在我的包裹中抛出这样的警告?

编辑:

这是一个可重现的示例。您必须重新启动 R 会话才能再次显示。如您所见,options(warn=2) 没有影响。

options(warn=2)
xx=c("Sepal.Width")
tidyselect::vars_select(names(iris), xx)

【问题讨论】:

  • 最好在下面发布答案,而不是将答案编辑到问题本身中。这允许其他人适当地支持潜在的解决方案。免费将您接受的答案更改为更新的首选解决方案。

标签: r lifecycle rlang


【解决方案1】:

对于tidyselect::vars_select,诀窍在于tidyselect:::inform_once

  if (env_has(inform_env, id)) {
    return(invisible(NULL))
  }
  inform_env[[id]] <- TRUE

  # ....

  inform(paste_line(
    msg, silver("This message is displayed once per session.")
  ))

维护了一个环境inform_env,记录给定消息是否已经显示。


lifecycle的情况下,它与deprecation_envdeprecate_warn中使用的环境类似

deprecate_warn <- function(....) {
  msg <- lifecycle_build_message(when, what, with, details, "deprecate_warn")

  # ....

  if (verbosity == "quiet") {
    return(invisible(NULL))
  }

  if (verbosity == "default" && !needs_warning(id) && ....) {
    return(invisible(NULL))
  }

  # ....

    if (verbosity == "default") {
      # Prevent warning from being displayed again
      env_poke(deprecation_env, id, Sys.time());

      msg <- paste_line(
        msg,
        silver("This warning is displayed once every 8 hours."),
        silver("Call `lifecycle::last_warnings()` to see where this warning was generated.")
      )
    }

    # ....
}

needs_warning <- function(id) {
  last <- deprecation_env[[id]]
  if (is_null(last)) {
    return(TRUE)
  }

  # ....

  # Warn every 8 hours
  (Sys.time() - last) > (8 * 60 * 60)
}

【讨论】:

  • 很好的答案,谢谢。可惜似乎没有内置功能,但我认为这是值得的。
【解决方案2】:

2021 年年中更新:

{rlang} 现在有一个内置选项。请参阅help here

rlang::warn("This message is displayed once per session.",   .frequency = "once")

原始答案:

虽然 Aurèle 的回答显然赢得了比赛,但 tidyselect 的函数并不完全适合我的需求,因为它需要一些未导出的函数。

对于希望在他们的包中使用简单功能的人,这是我的:

#' @importFrom rlang env env_has inform
#' @importFrom crayon silver has_color
#' @author tidyselect (https://github.com/r-lib/tidyselect/blob/2fab83639982d37fd94914210f771ab9cbd36b4b/R/utils.R#L281)
warning_once = function(msg, id=msg) {
    stopifnot(is_string(id))
    
    if (env_has(warning_env, id)) {
        return(invisible(NULL))
    }
    inform_env[[id]] = TRUE
    
    x = "This message is displayed once per session."
    if(is_installed("crayon") && crayon::has_color())
        x=crayon::silver(x)
    warn(paste(msg, x, sep = "\n"))
}
warning_env = rlang::env()

【讨论】:

    猜你喜欢
    • 2013-06-05
    • 2020-10-08
    • 1970-01-01
    • 2017-04-25
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多