| Title: | Code Storage and Execution Class for 'teal' Applications |
|---|---|
| Description: | Introduction of 'qenv' S4 class, that facilitates code execution and reproducibility in 'teal' applications. |
| Authors: | Dawid Kaledkowski [aut, cre], Aleksander Chlebowski [aut], Marcin Kosinski [aut], Pawel Rucki [aut], Nikolas Burkoff [aut], Mahmoud Hallal [aut], Maciej Nasinski [aut], Konrad Pagacz [aut], Junlue Zhao [aut], Chendi Liao [rev], Dony Unardi [rev], F. Hoffmann-La Roche AG [cph, fnd] |
| Maintainer: | Dawid Kaledkowski <[email protected]> |
| License: | Apache License 2.0 |
| Version: | 0.7.1 |
| Built: | 2026-05-21 05:57:31 UTC |
| Source: | https://github.com/insightsengineering/teal.code |
qenv objects## S3 method for class 'qenv' c(...) ## S3 method for class 'qenv.error' c(...) join(...)## S3 method for class 'qenv' c(...) ## S3 method for class 'qenv.error' c(...) join(...)
... |
function is deprecated. |
q <- qenv() q1 <- within(q, { iris1 <- iris mtcars1 <- mtcars }) q1 <- within(q1, iris2 <- iris) q2 <- within(q1, mtcars2 <- mtcars) qq <- c(q1, q2) cat(get_code(qq))q <- qenv() q1 <- within(q, { iris1 <- iris mtcars1 <- mtcars }) q1 <- within(q1, iris2 <- iris) q2 <- within(q1, mtcars2 <- mtcars) qq <- c(q1, q2) cat(get_code(qq))
qenv objectsCombine two qenv objects by simple concatenate their environments and the code.
concat(x, y)concat(x, y)
x |
( |
y |
( |
We recommend to use the join method to have a stricter control
in case x and y contain duplicated bindings and code.
RHS argument content has priority over the LHS one.
qenv object.
q <- qenv() q1 <- eval_code(q, expression(iris1 <- iris, mtcars1 <- mtcars)) q2 <- q1 q1 <- eval_code(q1, "iris2 <- iris") q2 <- eval_code(q2, "mtcars2 <- mtcars") qq <- concat(q1, q2) get_code(qq)q <- qenv() q1 <- eval_code(q, expression(iris1 <- iris, mtcars1 <- mtcars)) q2 <- q1 q1 <- eval_code(q1, "iris2 <- iris") q2 <- eval_code(q2, "mtcars2 <- mtcars") qq <- concat(q1, q2) get_code(qq)
This function opens a PDF graphics device using grDevices::pdf to suppress
the plot display in the IDE. The purpose of this function is to avoid opening graphic devices
directly in the IDE.
dev_suppress(x)dev_suppress(x)
x |
lazy binding which generates the plot(s) |
The function uses base::on.exit to ensure that the PDF graphics
device is closed (using grDevices::dev.off) when the function exits,
regardless of whether it exits normally or due to an error. This is necessary to
clean up the graphics device properly and avoid any potential issues.
No return value, called for side effects.
dev_suppress(plot(1:10))dev_suppress(plot(1:10))
qenv
Evaluate code in qenv
eval_code(object, code, ...)eval_code(object, code, ...)
object |
( |
code |
( |
... |
( |
eval_code() evaluates given code in the qenv environment and appends it to the code slot.
Thus, if the qenv had been instantiated empty, contents of the environment are always a result of the stored code.
qenv environment with code/expr evaluated or qenv.error if evaluation fails.
# evaluate code in qenv q <- qenv() q <- eval_code(q, "a <- 1") q <- eval_code(q, "b <- 2L # with comment") q <- eval_code(q, quote(library(checkmate))) q <- eval_code(q, expression(assert_number(a)))# evaluate code in qenv q <- qenv() q <- eval_code(q, "a <- 1") q <- eval_code(q, "b <- 2L # with comment") q <- eval_code(q, quote(library(checkmate))) q <- eval_code(q, expression(assert_number(a)))
qenv
Retrieves the code stored in the qenv.
get_code(object, deparse = TRUE, names = NULL, ...)get_code(object, deparse = TRUE, names = NULL, ...)
The code used in the qenv in the form specified by deparse.
get_code(object, names) limits the returned code to contain only those lines needed to create
the requested objects. The code stored in the qenv is analyzed statically to determine
which lines the objects of interest depend upon. The analysis works well when objects are created
with standard infix assignment operators (see ?assignOps) but it can fail in some situations.
Consider the following examples:
Case 1: Usual assignments.
q1 <-
within(qenv(), {
foo <- function(x) {
x + 1
}
x <- 0
y <- foo(x)
})
get_code(q1, names = "y")
x has no dependencies, so get_code(data, names = "x") will return only the second call.y depends on x and foo, so get_code(data, names = "y") will contain all three calls.
Case 2: Some objects are created by a function's side effects.
q2 <-
within(qenv(){
foo <- function() {
x <<- x + 1
}
x <- 0
foo()
y <- x
})
get_code(q2, names = "y")
Here, y depends on x but x is modified by foo as a side effect (not by reassignment)
and so get_code(data, names = "y") will not return the foo() call.
To overcome this limitation, code dependencies can be specified manually.
Lines where side effects occur can be flagged by adding "# @linksto <object name>" at the end.
Note that within evaluates code passed to expr as is and comments are ignored.
In order to include comments in code one must use the eval_code function instead.
q3 <-
eval_code(qenv(), "
foo <- function() {
x <<- x + 1
}
x <- 0
foo() # @linksto x
y <- x
")
get_code(q3, names = "y")
Now the foo() call will be properly included in the code required to recreate y.
Note that two functions that create objects as side effects, assign and data, are handled automatically.
Here are known cases where manual tagging is necessary:
non-standard assignment operators, e.g. %<>%
objects used as conditions in if statements: if (<condition>)
objects used to iterate over in for loops: for(i in <sequence>)
creating and evaluating language objects, e.g. eval(<call>)
# retrieve code q <- within(qenv(), { a <- 1 b <- 2 }) get_code(q) get_code(q, deparse = FALSE) get_code(q, names = "a") q <- qenv() q <- eval_code(q, code = c("a <- 1", "b <- 2")) get_code(q, names = "a")# retrieve code q <- within(qenv(), { a <- 1 b <- 2 }) get_code(q) get_code(q, deparse = FALSE) get_code(q, names = "a") q <- qenv() q <- eval_code(q, code = c("a <- 1", "b <- 2")) get_code(q, names = "a")
qenv
The access of environment included in the qenv that contains all data objects.
get_env(object)get_env(object)
object |
( |
An environment stored in qenv with all data objects.
q <- qenv() q1 <- within(q, { a <- 5 b <- data.frame(x = 1:10) }) get_env(q1)q <- qenv() q1 <- within(q, { a <- 5 b <- data.frame(x = 1:10) }) get_env(q1)
qenv objectRetrieve all messages raised during code evaluation in a qenv.
get_messages(object)get_messages(object)
object |
( |
character containing warning information or NULL if no messages.
data_q <- qenv() data_q <- eval_code(data_q, "iris_data <- iris") warning_qenv <- eval_code( data_q, bquote(p <- hist(iris_data[, .("Sepal.Length")], ff = "")) ) cat(get_messages(warning_qenv))data_q <- qenv() data_q <- eval_code(data_q, "iris_data <- iris") warning_qenv <- eval_code( data_q, bquote(p <- hist(iris_data[, .("Sepal.Length")], ff = "")) ) cat(get_messages(warning_qenv))
eval_code evaluates code silently so plots and prints don't show up in the console or graphic devices.
If one wants to use an output outside of the qenv (e.g. use a graph in renderPlot) then use get_outputs.
get_outputs(object)get_outputs(object)
object |
( |
list of outputs generated in a 'qenv“
q <- eval_code( qenv(), quote({ a <- 1 print("I'm an output") plot(1) }) ) get_outputs(q)q <- eval_code( qenv(), quote({ a <- 1 print("I'm an output") plot(1) }) ) get_outputs(q)
qenv
Instead of
get_var() use native R operators/functions:
x[[name]], x$name or get():
get_var(...) ## S3 method for class 'qenv.error' x[[i]]get_var(...) ## S3 method for class 'qenv.error' x[[i]]
... |
function is deprecated. |
x |
( |
i |
( |
qenv objectRetrieve all warnings raised during code evaluation in a qenv.
get_warnings(object)get_warnings(object)
object |
( |
character containing warning information or NULL if no warnings.
data_q <- qenv() data_q <- eval_code(data_q, "iris_data <- iris") warning_qenv <- eval_code( data_q, bquote(p <- hist(iris_data[, .("Sepal.Length")], ff = "")) ) cat(get_warnings(warning_qenv))data_q <- qenv() data_q <- eval_code(data_q, "iris_data <- iris") warning_qenv <- eval_code( data_q, bquote(p <- hist(iris_data[, .("Sepal.Length")], ff = "")) ) cat(get_warnings(warning_qenv))
qenv environmentInstantiates a qenv environment.
qenv()qenv()
qenv class has following characteristics:
It inherits from the environment and methods such as $, get(), ls(), as.list(),
parent.env() work out of the box.
qenv is a locked environment, and data modification is only possible through the eval_code()
and within.qenv() functions.
It stores metadata about the code used to create the data (see get_code()).
It supports slicing (see subset-qenv)
It is immutable which means that each code evaluation does not modify the original qenv
environment directly. See the following code:
q1 <- qenv() q2 <- eval_code(q1, "a <- 1") identical(q1, q2) # FALSE
qenv environment.
eval_code(), get_var(), subset-qenv, get_env(),get_warnings(), join(), concat()
q <- qenv() q2 <- within(q, a <- 1) ls(q2) q2$aq <- qenv() q2 <- within(q, a <- 1) ls(q2) q2$a
qenv objectPrints the qenv object.
## S4 method for signature 'qenv' show(object)## S4 method for signature 'qenv' show(object)
object |
( |
object, invisibly.
q <- qenv() q1 <- eval_code(q, expression(a <- 5, b <- data.frame(x = 1:10))) q1q <- qenv() q1 <- eval_code(q, expression(a <- 5, b <- data.frame(x = 1:10))) q1
qenv
Subsets qenv environment and limits the code to the necessary needed to build limited objects.
## S3 method for class 'qenv' x[names, ...]## S3 method for class 'qenv' x[names, ...]
x |
( |
names |
( |
... |
internal usage, please ignore. |
q <- qenv() q <- eval_code(q, "a <- 1;b<-2") q["a"] q[c("a", "b")]q <- qenv() q <- eval_code(q, "a <- 1;b<-2") q["a"] q[c("a", "b")]
qenv
Evaluate code in qenv
## S3 method for class 'qenv' within(data, expr, ...)## S3 method for class 'qenv' within(data, expr, ...)
data |
( |
expr |
( |
... |
named argument value will substitute a symbol in the |
within() is a convenience method that wraps eval_code to provide a simplified way of passing expression.
within accepts only inline expressions (both simple and compound) and allows to substitute expr
with ... named argument values.
Functions that trigger side effects like options or set.seed can be
linked to specific objects for further code retrieval (with get_code), but
only through eval_code where code input as character. within works on
expressions that do not preserve comments, hence you can not use # @linksto tag explained in get_code.
within
Passing language objects to expr is generally not intended but can be achieved with do.call.
Only single expressions will work and substitution is not available. See examples.
# evaluate code using within q <- qenv() q <- within(q, { i <- iris }) q <- within(q, { m <- mtcars f <- faithful }) q get_code(q) # inject values into code q <- qenv() q <- within(q, i <- iris) within(q, print(dim(subset(i, Species == "virginica")))) within(q, print(dim(subset(i, Species == species)))) # fails within(q, print(dim(subset(i, Species == species))), species = "versicolor") species_external <- "versicolor" within(q, print(dim(subset(i, Species == species))), species = species_external) # pass language objects expr <- expression(i <- iris, m <- mtcars) within(q, expr) # fails do.call(within, list(q, expr)) exprlist <- list(expression(i <- iris), expression(m <- mtcars)) within(q, exprlist) # fails do.call(within, list(q, do.call(c, exprlist)))# evaluate code using within q <- qenv() q <- within(q, { i <- iris }) q <- within(q, { m <- mtcars f <- faithful }) q get_code(q) # inject values into code q <- qenv() q <- within(q, i <- iris) within(q, print(dim(subset(i, Species == "virginica")))) within(q, print(dim(subset(i, Species == species)))) # fails within(q, print(dim(subset(i, Species == species))), species = "versicolor") species_external <- "versicolor" within(q, print(dim(subset(i, Species == species))), species = species_external) # pass language objects expr <- expression(i <- iris, m <- mtcars) within(q, expr) # fails do.call(within, list(q, expr)) exprlist <- list(expression(i <- iris), expression(m <- mtcars)) within(q, exprlist) # fails do.call(within, list(q, do.call(c, exprlist)))