File: handlers.R

package info (click to toggle)
r-cran-progressr 0.15.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,132 kB
  • sloc: sh: 13; makefile: 7
file content (172 lines) | stat: -rw-r--r-- 5,941 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#' Control How Progress is Reported
#'
#' @param \dots One or more progression handlers.  Alternatively, this
#' functions accepts also a single vector of progression handlers as input.
#' If this vector is empty, then an empty set of progression handlers will
#' be set.
#'
#' @param append (logical) If FALSE, the specified progression handlers
#' replace the current ones, otherwise appended to them.
#'
#' @param on_missing (character) If `"error"`, an error is thrown if one of
#' the progression handlers does not exists.  If `"warning"`, a warning
#' is produces and the missing handlers is ignored.  If `"ignore"`, the
#' missing handlers is ignored.
#'
#' @param default The default progression calling handler to use if none
#' are set.
#'
#' @param global If TRUE, then the global progression handler is enabled.
#' If FALSE, it is disabled.  If NA, then TRUE is returned if it is enabled,
#' otherwise FALSE.  Argument `global` must not used with other arguments.
#'
#' @return (invisibly) the previous list of progression handlers set.
#' If no arguments are specified, then the current set of progression
#' handlers is returned.
#' If `global` is specified, then TRUE is returned if the global progression
#' handlers is enabled, otherwise false.
#'
#' @details
#' This function provides a convenient alternative for getting and setting
#' option \option{progressr.handlers}.
#'
#' @section For package developers:
#' **IMPORTANT: Setting progression handlers is a privilege that should be
#' left to the end user. It should not be used by R packages, which only task
#' is to _signal_ progress updates, not to decide if, when, and how progress
#' should be reported.**
#'
#' If you have to set or modify the progression handlers inside a function,
#' please make sure to undo the settings afterward.  If not, you will break
#' whatever progression settings the user already has for other purposes
#' used elsewhere.  To undo you settings, you can do:
#'
#' ```r
#' old_handlers <- handlers(c("beepr", "progress"))
#' on.exit(handlers(old_handlers), add = TRUE)
#' ```
#'
#' @section Configuring progression handling during R startup:
#' A convenient place to configure the default progression handler and to
#' enable global progression reporting by default is in the \file{~/.Rprofile}
#' startup file.  For example, the following will (i) cause your interactive
#' R session to use global progression handler by default, and (ii) report
#' progress via the \pkg{progress} package when in the terminal and via the
#' RStudio Jobs progress bar when in the RStudio Console.
#' [handler_txtprogressbar], 
#' other whenever using the RStudio Console, add
#' the following to your \file{~/.Rprofile} startup file:
#'
#' ```r
#' if (interactive() && requireNamespace("progressr", quietly = TRUE)) {
#'   ## Enable global progression updates
#'   if (getRversion() >= 4) progressr::handlers(global = TRUE)
#'
#'   ## In RStudio Console, or not?
#'   if (Sys.getenv("RSTUDIO") == "1" && !nzchar(Sys.getenv("RSTUDIO_TERM"))) {
#'     options(progressr.handlers = progressr::handler_rstudio)
#'   } else {
#'     options(progressr.handlers = progressr::handler_progress)
#'   }
#' }
#' ```
#'
#' @example incl/handlers.R
#'
#' @export
handlers <- function(..., append = FALSE, on_missing = c("error", "warning", "ignore"), default = handler_txtprogressbar, global = NULL) {
  stop_if_not(
    is.null(global) ||
    ( is.logical(global) && length(global) == 1L )
  )
  args <- list(...)
  nargs <- length(args)

  if (nargs == 0L) {
    ## Get the current set of progression handlers?
    if (is.null(global)) {
      if (!is.list(default) && !is.null(default)) default <- list(default)
      return(getOption("progressr.handlers", default))
    }

    ## Check, register, or reset global calling handlers?
    if (is.na(global)) {
      return(register_global_progression_handler(action = "query"))
    }
    action <- if (isTRUE(global)) "add" else "remove"
    return(invisible(register_global_progression_handler(action = action)))
  }

  if (!is.null(global)) {
    stop("Argument 'global' must not be specified when also registering progress handlers")
  }

  on_missing <- match.arg(on_missing)
  
  ## Was a list specified?
  if (nargs == 1L && is.vector(args[[1]])) {
    args <- args[[1]]
  }

  handlers <- list()
  
  names <- names(args)
  for (kk in seq_along(args)) {
    handler <- args[[kk]]
    stop_if_not(length(handler) == 1L)
    
    if (is.character(handler)) {
      name <- handler
      name2 <- sprintf("handler_%s", name)
      
      handler <- NULL
      if (exists(name2, mode = "function")) {
        handler <- get(name2, mode = "function")
      }
      
      if (is.null(handler)) {
        if (exists(name, mode = "function")) {
          handler <- get(name, mode = "function")
        }
      }
      
      if (is.null(handler)) {
        if (on_missing == "error") {
          stop("No such progression handler found: ", sQuote(name))
	} else if (on_missing == "warning") {
          warning("Ignoring non-existing progression handler: ", sQuote(name))
	}
        next
      }
    } else {
      name <- NULL
    }
    stop_if_not(is.function(handler), length(formals(handler)) >= 1L)

    ## Validate?
    validator <- attr(handler, "validator")
    if (is.function(validator)) {
      is_valid <- validator()
      if (!is_valid) next
    }

    if (!is.null(name)) names[kk] <- name
    handlers[[kk]] <- handler
  }
  stop_if_not(is.list(handlers))
  names(handlers) <- names

  ## Drop non-existing handlers
  keep <- vapply(handlers, FUN.VALUE = FALSE, FUN = is.function)
  handlers <- handlers[keep]

  if (append) {
    current <- getOption("progressr.handlers", list())
    if (length(current) > 0L) handlers <- c(current, handlers)
  }

  old_handlers <- options(progressr.handlers = handlers)[[1]]
  if (is.null(old_handlers)) old_handlers <- list()
  
  invisible(old_handlers)
}