Skip to content

Commit 79279da

Browse files
committed
evaluate caches outputs
1 parent 5a26afd commit 79279da

File tree

9 files changed

+122
-61
lines changed

9 files changed

+122
-61
lines changed

.lintr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
linters: linters_with_defaults(
22
line_length_linter = line_length_linter(120),
3-
cyclocomp_linter = NULL,
43
object_usage_linter = NULL
54
)

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Depends:
2929
Imports:
3030
checkmate (>= 2.1.0),
3131
cli (>= 3.4.0),
32+
evaluate (>= 1.0.0),
3233
grDevices,
3334
lifecycle (>= 0.2.0),
3435
rlang (>= 1.1.0),

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### Miscellaneous
88

9+
* `eval_code` uses `evaluate::evaluate` and stores returned outputs in the code's attribute.
910
* Refactor `eval_code` method signature to allow for more flexibility when extending the `eval_code`/`within` functions.
1011

1112
# teal.code 0.6.1

R/qenv-eval_code.R

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,48 +53,44 @@ setMethod("eval_code", signature = c(object = "qenv.error"), function(object, co
5353
return(object)
5454
}
5555
code_split <- split_code(paste(code, collapse = "\n"))
56+
5657
for (i in seq_along(code_split)) {
5758
current_code <- code_split[[i]]
5859
current_call <- parse(text = current_code, keep.source = TRUE)
59-
# Using withCallingHandlers to capture warnings and messages.
60-
# Using tryCatch to capture the error and abort further evaluation.
61-
x <- withCallingHandlers(
62-
tryCatch(
63-
{
64-
eval(current_call, envir = object@.xData)
65-
if (!identical(parent.env(object@.xData), parent.env(.GlobalEnv))) {
66-
# needed to make sure that @.xData is always a sibling of .GlobalEnv
67-
# could be changed when any new package is added to search path (through library or require call)
68-
parent.env(object@.xData) <- parent.env(.GlobalEnv)
69-
}
70-
NULL
71-
},
72-
error = function(e) {
73-
errorCondition(
74-
message = sprintf(
75-
"%s \n when evaluating qenv code:\n%s",
76-
cli::ansi_strip(conditionMessage(e)),
77-
current_code
78-
),
79-
class = c("qenv.error", "try-error", "simpleError"),
80-
trace = unlist(c(object@code, list(current_code)))
81-
)
82-
}
83-
),
84-
warning = function(w) {
85-
attr(current_code, "warning") <<- cli::ansi_strip(sprintf("> %s\n", conditionMessage(w)))
86-
invokeRestart("muffleWarning")
87-
},
88-
message = function(m) {
89-
attr(current_code, "message") <<- cli::ansi_strip(sprintf("> %s", conditionMessage(m)))
90-
invokeRestart("muffleMessage")
91-
}
60+
x <- evaluate::evaluate(
61+
current_code,
62+
envir = object@.xData,
63+
stop_on_error = 1,
64+
output_handler = evaluate::new_output_handler(value = identity)
9265
)
9366

94-
if (!is.null(x)) {
95-
return(x)
67+
e <- Filter(function(e) inherits(e, "error"), x)
68+
if (length(e)) {
69+
return(
70+
errorCondition(
71+
message = sprintf(
72+
"%s \n when evaluating qenv code:\n%s",
73+
cli::ansi_strip(conditionMessage(e[[1]])),
74+
current_code
75+
),
76+
class = c("qenv.error", "try-error", "simpleError"),
77+
trace = unlist(c(object@code, list(current_code)))
78+
)
79+
)
80+
}
81+
if (!identical(parent.env(object@.xData), parent.env(.GlobalEnv))) {
82+
# needed to make sure that @.xData is always a sibling of .GlobalEnv
83+
# could be changed when any new package is added to search path (through library or require call)
84+
parent.env(object@.xData) <- parent.env(.GlobalEnv)
9685
}
97-
attr(current_code, "dependency") <- extract_dependency(current_call)
86+
87+
attributes(current_code) <- Filter(
88+
length,
89+
list(
90+
dependency = extract_dependency(current_call),
91+
outputs = x[-1]
92+
)
93+
)
9894
object@code <- c(object@code, stats::setNames(list(current_code), sample.int(.Machine$integer.max, size = 1)))
9995
}
10096

R/utils.R

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,25 @@ lang2calls <- function(x) {
5757
#' Obtain warnings or messages from code slot
5858
#'
5959
#' @param object (`qenv`)
60-
#' @param what (`"warning"` or `"message"`)
60+
#' @param what (`warning` or `message`)
6161
#' @return `character(1)` containing combined message or `NULL` when no warnings/messages
6262
#' @keywords internal
6363
get_warn_message_util <- function(object, what) {
6464
checkmate::matchArg(what, choices = c("warning", "message"))
65-
messages <- lapply(object@code, "attr", what)
65+
messages <- lapply(
66+
object@code,
67+
function(x) {
68+
unlist(lapply(
69+
attr(x, "outputs"),
70+
function(el) {
71+
if (inherits(el, what)) {
72+
sprintf("> %s", conditionMessage(el))
73+
}
74+
}
75+
))
76+
}
77+
)
78+
6679
idx_warn <- which(sapply(messages, function(x) !is.null(x) && !identical(x, "")))
6780
if (!any(idx_warn)) {
6881
return(NULL)
@@ -74,7 +87,7 @@ get_warn_message_util <- function(object, what) {
7487
warn = messages,
7588
expr = code,
7689
function(warn, expr) {
77-
sprintf("%swhen running code:\n%s", warn, expr)
90+
sprintf("%s\nwhen running code:\n%s", trimws(warn), trimws(expr))
7891
}
7992
)
8093

man/get_warn_message_util.Rd

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

tests/testthat/test-qenv_concat.R

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,20 @@ testthat::test_that("Concatenate two independent qenvs with warnings results in
5050

5151
q12 <- concat(q1, q2)
5252

53-
testthat::expect_equal(
54-
unlist(lapply(q12@code, attr, "warning"), use.names = FALSE),
55-
c(
56-
"> This is warning 1\n",
57-
"> This is warning 2\n"
53+
testthat::expect_identical(
54+
get_warnings(q12),
55+
paste(
56+
"~~~ Warnings ~~~",
57+
"\n> This is warning 1",
58+
"when running code:",
59+
"warning('This is warning 1')",
60+
"\n> This is warning 2",
61+
"when running code:",
62+
"warning('This is warning 2')",
63+
"\n~~~ Trace ~~~\n",
64+
"warning('This is warning 1')",
65+
"warning('This is warning 2')",
66+
sep = "\n"
5867
)
5968
)
6069
})
@@ -65,11 +74,20 @@ testthat::test_that("Concatenate two independent qenvs with messages results in
6574

6675
q12 <- concat(q1, q2)
6776

68-
testthat::expect_equal(
69-
unlist(lapply(q12@code, attr, "message"), use.names = FALSE),
70-
c(
71-
"> This is message 1\n",
72-
"> This is message 2\n"
77+
testthat::expect_identical(
78+
get_messages(q12),
79+
paste(
80+
"~~~ Messages ~~~",
81+
"\n> This is message 1",
82+
"when running code:",
83+
"message('This is message 1')",
84+
"\n> This is message 2",
85+
"when running code:",
86+
"message('This is message 2')",
87+
"\n~~~ Trace ~~~\n",
88+
"message('This is message 1')",
89+
"message('This is message 2')",
90+
sep = "\n"
7391
)
7492
)
7593
})

tests/testthat/test-qenv_eval_code.R

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,18 @@ testthat::test_that("comments passed alone to eval_code that contain @linksto ta
186186
"x"
187187
)
188188
})
189+
190+
testthat::test_that("object printed (explicitly) is stored as string in the 'outputs' attribute of a code element", {
191+
q <- eval_code(qenv(), "print('whatever')")
192+
testthat::expect_identical(attr(q@code[[1]], "outputs")[[1]], '[1] "whatever"\n')
193+
})
194+
195+
testthat::test_that("object printed (implicitly) is stored asis in the 'outputs' attribute of a code element", {
196+
q <- eval_code(qenv(), "head(letters)")
197+
testthat::expect_identical(attr(q@code[[1]], "outputs")[[1]], head(letters))
198+
})
199+
200+
testthat::test_that("plot output is stored as recordedplot in the 'outputs' attribute of a code element", {
201+
q <- eval_code(qenv(), "plot(1)")
202+
testthat::expect_s3_class(attr(q@code[[1]], "outputs")[[1]], "recordedplot")
203+
})

tests/testthat/test-qenv_join.R

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,20 @@ testthat::test_that("Joining two independent qenvs with warnings results in obje
130130

131131
q <- c(q1, q2)
132132

133-
testthat::expect_equal(
134-
vapply(q@code, attr, which = "warning", character(1L), USE.NAMES = FALSE),
135-
c(
136-
"> This is warning 1\n",
137-
"> This is warning 2\n"
133+
testthat::expect_identical(
134+
get_warnings(q),
135+
paste(
136+
"~~~ Warnings ~~~",
137+
"\n> This is warning 1",
138+
"when running code:",
139+
"warning('This is warning 1')",
140+
"\n> This is warning 2",
141+
"when running code:",
142+
"warning('This is warning 2')",
143+
"\n~~~ Trace ~~~\n",
144+
"warning('This is warning 1')",
145+
"warning('This is warning 2')",
146+
sep = "\n"
138147
)
139148
)
140149
})
@@ -145,11 +154,20 @@ testthat::test_that("Joining two independent qenvs with messages results in obje
145154

146155
q <- c(q1, q2)
147156

148-
testthat::expect_equal(
149-
vapply(q@code, attr, which = "message", character(1L), USE.NAMES = FALSE),
150-
c(
151-
"> This is message 1\n",
152-
"> This is message 2\n"
157+
testthat::expect_identical(
158+
get_messages(q),
159+
paste(
160+
"~~~ Messages ~~~",
161+
"\n> This is message 1",
162+
"when running code:",
163+
"message('This is message 1')",
164+
"\n> This is message 2",
165+
"when running code:",
166+
"message('This is message 2')",
167+
"\n~~~ Trace ~~~\n",
168+
"message('This is message 1')",
169+
"message('This is message 2')",
170+
sep = "\n"
153171
)
154172
)
155173
})

0 commit comments

Comments
 (0)