Как отображать предупреждение только один раз за сеанс? - PullRequest
4 голосов
/ 07 апреля 2020

В моем пакете есть функциональность, которую следует использовать с осторожностью.

Пользователь должен знать об этом, но если он / она думает, что ситуация в порядке, то было бы неудобно показывать предупреждение каждый раз, когда вызывается функция.

Я часто вижу предупреждения, которые отображаются только один раз. Их довольно сложно отлаживать, поэтому я не смог найти воспроизводимый пример (я добавлю один, если я его получу), но они показывают специальное c предупреждающее сообщение, за которым следует rlang info:

Это предупреждение отображается один раз за сеанс

Требуется много помощи для отладки этих сообщений (например, здесь , здесь , или здесь , просто Google "r Это предупреждение отображается один раз за сеанс")

Я думаю, что пакет lifecyle часто использует их для мягкого устаревания, но я не смог чтобы обнаружить трюк в lifecycle:::lifecycle_build_message.

Как я могу выбросить такое предупреждение в мою посылку?

РЕДАКТИРОВАТЬ:

Вот воспроизводимый пример. Вы должны перезапустить сеанс R, чтобы он снова появился. Как видите, options(warn=2) не оказал влияния.

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

Ответы [ 2 ]

3 голосов
/ 07 апреля 2020

В случае 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_env используется в deprecate_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)
}
1 голос
/ 07 апреля 2020

Хотя ответ 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()
...