Skip to content

Commit dbe4896

Browse files
committed
Merge branch 'main' into rc-v1.7.2
2 parents 1c9f894 + ff5ef52 commit dbe4896

File tree

5 files changed

+67
-27
lines changed

5 files changed

+67
-27
lines changed

NEWS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ shiny 1.7.2
2323

2424
### Bug fixes
2525

26+
* Closed #3250:`{rlang}`/`{tidyeval}` conditions (i.e., warnings and errors) are no longer filtered from stack traces. (#3602)
27+
28+
* Closed #3581: Errors in throttled/debounced reactive expressions no longer cause the session to exit. (#3624)
29+
2630
* The auto-reload feature (`options(shiny.autoreload=TRUE)`) was not being activated by `devmode(TRUE)`, despite a console message asserting that it was. (#3620)
2731

2832
* Closed #2297: If an error occurred in parsing a value in a bookmark query string, an error would be thrown and nothing would be restored. Now a message is displayed and that value is ignored. (Thanks to @daattali, #3385)
@@ -43,7 +47,6 @@ shiny 1.7.2
4347

4448
* HTML dependencies that are sent to dynamic UI now have better type checking, and no longer require a `dep.src.href` field. (#3537)
4549

46-
4750
shiny 1.7.1
4851
===========
4952

R/conditions.R

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,17 @@ pruneStackTrace <- function(parents) {
421421
# Loop over the parent indices. Anything that is not parented by current_node
422422
# (a.k.a. last-known-good node), or is a dupe, can be discarded. Anything that
423423
# is kept becomes the new current_node.
424+
#
425+
# jcheng 2022-03-18: Two more reasons a node can be kept:
426+
# 1. parent is 0
427+
# 2. parent is i
428+
# Not sure why either of these situations happen, but they're common when
429+
# interacting with rlang/dplyr errors. See issue rstudio/shiny#3250 for repro
430+
# cases.
424431
include <- vapply(seq_along(parents), function(i) {
425-
if (!is_dupe[[i]] && parents[[i]] == current_node) {
432+
if ((!is_dupe[[i]] && parents[[i]] == current_node) ||
433+
parents[[i]] == 0 ||
434+
parents[[i]] == i) {
426435
current_node <<- i
427436
TRUE
428437
} else {

R/reactives.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,11 +2472,11 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
24722472

24732473
# Ensure r() is called only after setting firstRun to FALSE since r()
24742474
# may throw an error
2475-
r()
2475+
try(r(), silent = TRUE)
24762476
return()
24772477
}
24782478
# This ensures r() is still tracked after firstRun
2479-
r()
2479+
try(r(), silent = TRUE)
24802480

24812481
# The value (or possibly millis) changed. Start or reset the timer.
24822482
v$when <- getDomainTimeMs(domain) + millis()
@@ -2509,7 +2509,7 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
25092509
# commenting it out and studying the unit test failure that results.
25102510
primer <- observe({
25112511
primer$destroy()
2512-
er()
2512+
try(er(), silent = TRUE)
25132513
}, label = "debounce primer", domain = domain, priority = priority)
25142514

25152515
er
@@ -2551,7 +2551,7 @@ throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
25512551
}
25522552

25532553
# Responsible for tracking when f() changes.
2554-
observeEvent(r(), {
2554+
observeEvent(try(r(), silent = TRUE), {
25552555
if (v$pending) {
25562556
# In a blackout period and someone already scheduled; do nothing
25572557
} else if (blackoutMillisLeft() > 0) {

tests/testthat/test-reactives.R

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,28 @@ test_that("can access reactive values directly", {
1313
y <- reactive(x1() + x2$a)
1414
expect_equal(y(), 4)
1515
})
16+
17+
test_that("errors in throttled/debounced reactives are catchable", {
18+
reactiveConsole(TRUE)
19+
on.exit(reactiveConsole(FALSE))
20+
21+
# In Shiny 1.7 and earlier, if a throttled/debounced reactive threw an error,
22+
# it would cause internal observers used by the implementations of
23+
# debounce/throttle to error, which would kill the session. The correct
24+
# behavior is to only expose the error to consumers of the throttled/debounced
25+
# reactive.
26+
27+
r <- reactive({
28+
stop("boom")
29+
})
30+
31+
rd <- r %>% debounce(1000)
32+
rt <- r %>% throttle(1000)
33+
34+
observe({
35+
try(rd(), silent = TRUE)
36+
try(rt(), silent = TRUE)
37+
})
38+
39+
expect_silent(flushReact())
40+
})
Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
1-
capture <- function() {
2-
list(
3-
calls = sys.calls(),
4-
parents = sys.parents()
5-
)
6-
}
1+
foo <- function() {
2+
capture <- function() {
3+
list(
4+
calls = sys.calls(),
5+
parents = sys.parents()
6+
)
7+
}
78

8-
capture_1 <- function() {
9-
capture()
10-
}
9+
capture_1 <- function() {
10+
capture()
11+
}
1112

12-
capture_2 <- function() {
13-
capture_1()
14-
}
13+
capture_2 <- function() {
14+
capture_1()
15+
}
1516

16-
res <- do.call(
17-
identity,
18-
list(
19-
identity(capture_2())
17+
do.call(
18+
identity,
19+
list(
20+
identity(capture_2())
21+
)
2022
)
21-
)
22-
res$calls <- tail(res$calls, 5)
23-
res$parents <- tail(res$parents - (length(res$parents) - 5), 5)
23+
}
24+
res <- foo()
25+
res$calls <- tail(res$calls, 6)
26+
res$parents <- tail(res$parents - (length(res$parents) - 6), 6)
2427

2528
describe("stack pruning", {
2629
it("passes basic example", {
27-
expect_equal(pruneStackTrace(res$parents), c(F, F, T, T, T))
28-
expect_equal(lapply(list(res$parents), pruneStackTrace), list(c(F, F, T, T, T)))
30+
expect_equal(pruneStackTrace(res$parents), c(T, F, F, T, T, T))
31+
expect_equal(lapply(list(res$parents), pruneStackTrace), list(c(T, F, F, T, T, T)))
2932
})
3033
})

0 commit comments

Comments
 (0)