Skip to content

Commit a8aeb4b

Browse files
Merge branch 'release/0.1.1'
2 parents b53ee4d + 6da9cac commit a8aeb4b

File tree

8 files changed

+175
-15
lines changed

8 files changed

+175
-15
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: progressr
2-
Version: 0.1.0
2+
Version: 0.1.1
33
Title: A Unifying API for Progress Updates
44
Description: A minimal API for reporting progress updates upstream. The design is to separate the representation of progress updates from how they are presented. What type of progress to signal is controlled by the developer. How these progress updates are rendered is controlled by the end user. For instance, some users may prefer visual feedback such as a horizontal progress bar in the terminal, whereas others may prefer auditory feedback.
55
Authors@R: c(

NEWS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Package: progressr
22
==================
33

4+
Version: 0.1.1 [2019-06-08]
5+
6+
NEW FEATURES:
7+
8+
* with_progress() now captures standard output and conditions and relay them
9+
at then end. This is done in order to avoid interweaving such output with
10+
the output produced by the progression handlers. This behavior can be
11+
controlled by arguments 'delay_stdout' and 'delay_condition'.
12+
13+
414
Version: 0.1.0 [2019-06-07]
515

616
NEW FEATURES:

R/slow_sum.R

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@
44
#'
55
#' @param delay Delay in seconds after each addition.
66
#'
7+
#' @param stdout If TRUE, then a text is outputted to the standard output
8+
#' per element.
9+
#'
10+
#' @param message If TRUE, then a message is outputted per element.
11+
#'
712
#' @return The sum of all elements in `x`.
813
#'
914
#' @section Progress updates:
1015
#' This function signals [progression] conditions as it progresses.
1116
#'
1217
#' @export
13-
slow_sum <- function(x, delay = getOption("delay", 0.05)) {
18+
slow_sum <- function(x, delay = getOption("delay", 0.05), stdout = FALSE, message = FALSE) {
1419
progress <- progressor(length(x))
15-
20+
1621
res <- 0
1722
for (kk in seq_along(x)) {
23+
if (stdout) cat(sprintf("Adding element #%d\n", kk))
1824
Sys.sleep(delay)
1925
res <- res + x[kk]
2026
progress(message = sprintf("Adding %g", kk))
27+
if (message) message(sprintf("Added value %g", x[kk]))
2128
}
2229

2330
res

R/utils.R

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# More efficient than the default utils::capture.output()
2+
#' @importFrom utils capture.output
3+
capture_output <- function(expr, envir = parent.frame(), ...) {
4+
res <- eval({
5+
file <- rawConnection(raw(0L), open = "w")
6+
on.exit(close(file))
7+
capture.output(expr, file = file)
8+
rawToChar(rawConnectionValue(file))
9+
}, envir = envir, enclos = baseenv())
10+
unlist(strsplit(res, split = "\n", fixed = TRUE), use.names = FALSE)
11+
}
12+
113
now <- function(x = Sys.time(), format = "[%H:%M:%OS3] ") {
214
format(as.POSIXlt(x, tz = ""), format = format)
315
}
@@ -12,16 +24,15 @@ mprintf <- function(..., appendLF = TRUE, debug = getOption("progressr.debug", F
1224
message(now(), sprintf(...), appendLF = appendLF)
1325
}
1426

15-
#' @importFrom utils capture.output
1627
mprint <- function(..., appendLF = TRUE, debug = getOption("progressr.debug", FALSE)) {
1728
if (!debug) return()
18-
message(paste(now(), capture.output(print(...)), sep = "", collapse = "\n"), appendLF = appendLF)
29+
message(paste(now(), capture_output(print(...)), sep = "", collapse = "\n"), appendLF = appendLF)
1930
}
2031

21-
#' @importFrom utils capture.output str
32+
#' @importFrom utils str
2233
mstr <- function(..., appendLF = TRUE, debug = getOption("progressr.debug", FALSE)) {
2334
if (!debug) return()
24-
message(paste(now(), capture.output(str(...)), sep = "", collapse = "\n"), appendLF = appendLF)
35+
message(paste(now(), capture_output(str(...)), sep = "", collapse = "\n"), appendLF = appendLF)
2536
}
2637

2738
stop_if_not <- function(...) {

R/with_progress.R

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@
88
#' @param cleanup If TRUE, all progression handlers will be shutdown
99
#' at the end regardless of the progression is complete or not.
1010
#'
11+
#' @param delay_stdout If TRUE, standard output is captured and relayed
12+
#' at the end just before any captured conditions are relayed.
13+
#'
14+
#' @param delay_conditions A character vector specifying [base::condition]
15+
#' classes to be captured and relayed at the end after any captured
16+
#' standard output is relayed.
17+
#'
1118
#' @return Return nothing (reserved for future usage).
1219
#'
1320
#' @example incl/with_progress.R
1421
#'
1522
#' @export
16-
with_progress <- function(expr, handlers = getOption("progressr.handlers", txtprogressbar_handler()), cleanup = TRUE) {
23+
with_progress <- function(expr, handlers = getOption("progressr.handlers", txtprogressbar_handler()), cleanup = TRUE, delay_stdout = TRUE, delay_conditions = c("condition")) {
1724
stop_if_not(is.logical(cleanup), length(cleanup) == 1L, !is.na(cleanup))
1825

1926
## FIXME: With zero handlers, progression conditions will be
@@ -59,16 +66,78 @@ with_progress <- function(expr, handlers = getOption("progressr.handlers", txtpr
5966
})
6067
}
6168

69+
## Captured stdout output and conditions
70+
stdout_file <- NULL
71+
conditions <- list()
72+
if (delay_stdout || length(delay_conditions) > 0) {
73+
## Delay standard output?
74+
if (delay_stdout) {
75+
stdout_file <- rawConnection(raw(0L), open = "w")
76+
sink(stdout_file, type = "output", split = FALSE)
77+
on.exit({
78+
sink(type = "output", split = FALSE)
79+
stdout <- rawToChar(rawConnectionValue(stdout_file))
80+
close(stdout_file)
81+
if (length(stdout) > 0) cat(stdout, file = stdout())
82+
})
83+
}
84+
85+
## Delay conditions?
86+
if (length(delay_conditions) > 0) {
87+
on.exit({
88+
if (length(conditions) > 0L) {
89+
for (kk in seq_along(conditions)) {
90+
c <- conditions[[kk]]
91+
if (inherits(c, "message")) {
92+
message(c)
93+
} else if (inherits(c, "warning")) {
94+
warning(c)
95+
} else if (inherits(c, "condition")) {
96+
signalCondition(c)
97+
}
98+
}
99+
}
100+
}, add = TRUE)
101+
}
102+
}
103+
62104
## Reset all handlers up start
63105
withCallingHandlers({
64-
withRestarts({
65-
signalCondition(control_progression("reset"))
66-
}, muffleProgression = function(p) NULL)
106+
withRestarts({
107+
signalCondition(control_progression("reset"))
108+
}, muffleProgression = function(p) NULL)
67109
}, progression = handler)
68110

69111
## Evaluate expression
70-
withCallingHandlers(expr, progression = handler)
71-
112+
withCallingHandlers(
113+
expr,
114+
progression = handler,
115+
condition = function(c) {
116+
if (inherits(c, c("progression", "error"))) return()
117+
if (inherits(c, delay_conditions)) {
118+
## Record
119+
conditions[[length(conditions) + 1L]] <<- c
120+
## Muffle
121+
if (inherits(c, "message")) {
122+
invokeRestart("muffleMessage")
123+
} else if (inherits(c, "warning")) {
124+
invokeRestart("muffleWarning")
125+
} else if (inherits(c, "condition")) {
126+
## If there is a "muffle" restart for this condition,
127+
## then invoke that restart, i.e. "muffle" the condition
128+
restarts <- computeRestarts(c)
129+
for (restart in restarts) {
130+
name <- restart$name
131+
if (is.null(name)) next
132+
if (!grepl("^muffle", name)) next
133+
invokeRestart(restart)
134+
break
135+
}
136+
}
137+
}
138+
}
139+
)
140+
72141
## Success
73142
status <- "ok"
74143

man/slow_sum.Rd

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/with_progress.Rd

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/with_progress,delay.R

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
library(progressr)
2+
3+
options(progressr.tests.fake_handlers = c("beepr_handler", "notifier_handler", "pbmcapply_handler", "tkprogressbar_handler", "winprogressbar_handler"))
4+
options(progressr.enable = TRUE)
5+
6+
options(delay = 0.001)
7+
options(progressr.times = +Inf)
8+
options(progressr.interval = 0)
9+
options(progressr.clear = FALSE)
10+
11+
record_output <- function(expr, envir = parent.frame()) {
12+
conditions <- list()
13+
stdout <- utils::capture.output({
14+
withCallingHandlers(
15+
expr,
16+
condition = function(c) {
17+
if (inherits(c, c("progression", "error"))) return()
18+
conditions[[length(conditions) + 1L]] <<- c
19+
}
20+
)
21+
}, split = TRUE)
22+
list(stdout = stdout, conditions = conditions)
23+
}
24+
25+
26+
message("*** with_progress() - delaying output ...")
27+
28+
x <- 1:5
29+
30+
## Record truth
31+
output_truth <- record_output({
32+
y_truth <- slow_sum(x, stdout=TRUE, message=TRUE)
33+
})
34+
35+
for (delay in c(FALSE, TRUE)) {
36+
message(sprintf("- with_progress() - delay = %s ...", delay))
37+
output <- record_output({
38+
with_progress({
39+
y <- slow_sum(x, stdout=TRUE, message=TRUE)
40+
}, delay_stdout = delay,
41+
delay_conditions = if (delay) "condition" else character(0L))
42+
})
43+
stopifnot(identical(output$stdout, output_truth$stdout))
44+
stopifnot(identical(output$conditions, output_truth$conditions))
45+
stopifnot(identical(y, y_truth))
46+
message(sprintf("- with_progress() - delay = %s ... DONE", delay))
47+
}
48+
49+
message("*** with_progress() - delaying output ... DONE")

0 commit comments

Comments
 (0)