Type: | Package |
Title: | Code Storage and Execution Class for 'teal' Applications |
Version: | 0.6.1 |
Date: | 2025-02-13 |
Description: | Introduction of 'qenv' S4 class, that facilitates code execution and reproducibility in 'teal' applications. |
License: | Apache License 2.0 |
URL: | https://insightsengineering.github.io/teal.code/, https://github.com/insightsengineering/teal.code |
BugReports: | https://github.com/insightsengineering/teal.code/issues |
Depends: | methods, R (≥ 4.0) |
Imports: | checkmate (≥ 2.1.0), cli (≥ 3.4.0), grDevices, lifecycle (≥ 0.2.0), rlang (≥ 1.1.0), stats, utils |
Suggests: | knitr (≥ 1.42), rmarkdown (≥ 2.23), shiny (≥ 1.6.0), testthat (≥ 3.1.8), withr (≥ 2.0.0) |
VignetteBuilder: | knitr, rmarkdown |
RdMacros: | lifecycle |
Config/Needs/verdepcheck: | mllg/checkmate, r-lib/cli, r-lib/lifecycle, r-lib/rlang, r-lib/cli, yihui/knitr, rstudio/rmarkdown, rstudio/shiny, r-lib/testthat, r-lib/withr |
Config/Needs/website: | insightsengineering/nesttemplate |
Encoding: | UTF-8 |
Language: | en-US |
RoxygenNote: | 7.3.2 |
Collate: | 'qenv-c.R' 'qenv-class.R' 'qenv-errors.R' 'qenv-concat.R' 'qenv-constructor.R' 'qenv-eval_code.R' 'qenv-extract.R' 'qenv-get_code.R' 'qenv-get_env.R' 'qenv-get_messages.r' 'qenv-get_var.R' 'qenv-get_warnings.R' 'qenv-join.R' 'qenv-length.R' 'qenv-show.R' 'qenv-within.R' 'teal.code-package.R' 'utils-get_code_dependency.R' 'utils.R' |
NeedsCompilation: | no |
Packaged: | 2025-02-14 01:12:45 UTC; unardid |
Author: | 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 <dawid.kaledkowski@roche.com> |
Repository: | CRAN |
Date/Publication: | 2025-02-14 05:40:05 UTC |
teal.code
: Code storage and execution class for teal applications
Description
The teal.code
package contains functionalities that can be used
to store and execute R calls. qenv
class allows to create a teal
module which returns code to reproduce application outputs.
Author(s)
Maintainer: Dawid Kaledkowski dawid.kaledkowski@roche.com
Authors:
Aleksander Chlebowski aleksander.chlebowski@contractors.roche.com
Marcin Kosinski marcin.kosinski.mk1@roche.com
Pawel Rucki pawel.rucki@roche.com
Nikolas Burkoff nikolas.burkoff@roche.com
Mahmoud Hallal mahmoud.hallal@roche.com
Maciej Nasinski maciej.nasinski@contractors.roche.com
Konrad Pagacz konrad.pagacz@contractors.roche.com
Junlue Zhao zhaoj88@gene.com
Other contributors:
Chendi Liao chendi.liao@roche.com [reviewer]
Dony Unardi unardid@gene.com [reviewer]
F. Hoffmann-La Roche AG [copyright holder, funder]
See Also
Useful links:
Report bugs at https://github.com/insightsengineering/teal.code/issues
If two qenv
can be joined
Description
Checks if two qenv
objects can be combined.
For more information, please see join
Usage
.check_joinable(x, y)
Arguments
x |
( |
y |
( |
Value
TRUE
if able to join or character
used to print error message.
Join qenv
objects
Description
Checks and merges two qenv
objects into one qenv
object.
The join()
function is superseded by the c()
function.
Usage
## S3 method for class 'qenv'
c(...)
## S3 method for class 'qenv.error'
c(...)
join(x, y)
Arguments
... |
( |
x |
( |
y |
( |
Details
Any common code at the start of the qenvs
is only placed once at the start of the joined qenv
.
This allows consistent behavior when joining qenvs
which share a common ancestor.
See below for an example.
There are some situations where join()
cannot be properly performed, such as these three scenarios:
Both
qenv
objects contain an object of the same name but are not identical.Example:
x <- eval_code(qenv(), expression(mtcars1 <- mtcars)) y <- eval_code(qenv(), expression(mtcars1 <- mtcars['wt'])) z <- c(x, y) # Error message will occur
In this example,
mtcars1
object exists in bothx
andy
objects but the content are not identical.mtcars1
in thex qenv
object has more columns thanmtcars1
in they qenv
object (only has one column).-
join()
will look for identical code elements in bothqenv
objects. The index position of these code elements must be the same to determine the evaluation order. Otherwise,join()
will throw an error message.Example:
common_q <- eval_code(qenv(), expression(v <- 1)) x <- eval_code( common_q, "x <- v" ) y <- eval_code( common_q, "y <- v" ) z <- eval_code( y, "z <- v" ) q <- c(x, y) join_q <- c(q, z) # Error message will occur # Check the order of evaluation based on the id slot
The error occurs because the index position of common code elements in the two objects is not the same.
The usage of temporary variable in the code expression could cause
join()
to fail.Example:
common_q <- qenv() x <- eval_code( common_q, "x <- numeric(0) for (i in 1:2) { x <- c(x, i) }" ) y <- eval_code( common_q, "y <- numeric(0) for (i in 1:3) { y <- c(y, i) }" ) q <- join(x,y) # Error message will occur # Check the value of temporary variable i in both objects x$i # Output: 2 y$i # Output: 3
c()
fails to provide a proper result because of the temporary variablei
exists in both objects but has different value. To fix this, we can seti <- NULL
in the code expression for both objects.common_q <- qenv() x <- eval_code( common_q, "x <- numeric(0) for (i in 1:2) { x <- c(x, i) } # dummy i variable to fix it i <- NULL" ) y <- eval_code( common_q, "y <- numeric(0) for (i in 1:3) { y <- c(y, i) } # dummy i variable to fix it i <- NULL" ) q <- c(x,y)
Value
qenv
object.
Examples
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 <- eval_code(q, expression(iris1 <- iris, mtcars1 <- mtcars))
q2 <- q1
q1 <- eval_code(q1, "iris2 <- iris")
q2 <- eval_code(q2, "mtcars2 <- mtcars")
qq <- join(q1, q2)
cat(get_code(qq))
common_q <- eval_code(q, quote(x <- 1))
y_q <- eval_code(common_q, quote(y <- x * 2))
z_q <- eval_code(common_q, quote(z <- x * 3))
join_q <- join(y_q, z_q)
# get_code only has "x <- 1" occurring once
cat(get_code(join_q))
Concatenate two qenv
objects
Description
Combine two qenv
objects by simple concatenate their environments and the code.
Usage
concat(x, y)
Arguments
x |
( |
y |
( |
Details
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.
Value
qenv
object.
Examples
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)
Suppresses plot display in the IDE by opening a PDF graphics device
Description
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.
Usage
dev_suppress(x)
Arguments
x |
lazy binding which generates the plot(s) |
Details
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.
Value
No return value, called for side effects.
Examples
dev_suppress(plot(1:10))
Evaluate code in qenv
Description
Evaluate code in qenv
Usage
eval_code(object, code)
## S3 method for class 'qenv'
within(data, expr, ...)
Arguments
object |
( |
code |
( |
data |
( |
expr |
( |
... |
named argument value will substitute a symbol in the |
Details
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.
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.
Value
qenv
environment with code/expr
evaluated or qenv.error
if evaluation fails.
Using language objects with within
Passing language objects to expr
is generally not intended but can be achieved with do.call
.
Only single expression
s will work and substitution is not available. See examples.
Examples
# 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 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)))
Get code from qenv
Description
Retrieves the code stored in the qenv
.
Usage
get_code(object, deparse = TRUE, names = NULL, ...)
Arguments
Value
The code used in the qenv
in the form specified by deparse
.
Extracting dataset-specific code
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>)
Examples
# 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")
Get code dependency of an object
Description
Extract subset of code required to reproduce specific object(s), including code producing side-effects.
Usage
get_code_dependency(code, names, check_code_names = TRUE)
Arguments
code |
|
names |
|
check_code_names |
|
Details
Given a character vector with code, this function will extract the part of the code responsible for creating
the variables specified by names
.
This includes the final call that creates the variable(s) in question as well as all parent calls,
i.e. calls that create variables used in the final call and their parents, etc.
Also included are calls that create side-effects like establishing connections.
It is assumed that object dependency is established by using three assignment operators: <-
, =
, and ->
.
Other assignment methods (assign
, <<-
) or non-standard-evaluation methods are not supported.
Side-effects are not detected automatically and must be marked in the code.
Add # @linksto object
at the end of a line where a side-effect occurs to specify that this line is required
to reproduce a variable called object
.
Value
Character vector, a subset of code
.
Note that subsetting is actually done on the calls code
, not necessarily on the elements of the vector.
Access environment included in qenv
Description
The access of environment included in the qenv
that contains all data objects.
Usage
get_env(object)
Arguments
object |
( |
Value
An environment
stored in qenv
with all data objects.
Examples
q <- qenv()
q1 <- within(q, {
a <- 5
b <- data.frame(x = 1:10)
})
get_env(q1)
Get messages from qenv
object
Description
Retrieve all messages raised during code evaluation in a qenv
.
Usage
get_messages(object)
Arguments
object |
( |
Value
character
containing warning information or NULL
if no messages.
Examples
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))
Get object from qenv
Description
Instead of
get_var()
use native R operators/functions:
x[[name]]
, x$name
or get()
:
Retrieve variables from the qenv
environment.
Usage
get_var(object, var)
## S3 method for class 'qenv.error'
x[[i]]
Arguments
object , x |
( |
var , i |
( |
Value
The value of required variable (var
) within qenv
object.
Examples
q <- qenv()
q1 <- eval_code(q, code = quote(a <- 1))
q2 <- eval_code(q1, code = "b <- a")
get_var(q2, "b")
Obtain warnings or messages from code slot
Description
Obtain warnings or messages from code slot
Usage
get_warn_message_util(object, what)
Arguments
object |
( |
what |
( |
Value
character(1)
containing combined message or NULL
when no warnings/messages
Get warnings from qenv
object
Description
Retrieve all warnings raised during code evaluation in a qenv
.
Usage
get_warnings(object)
Arguments
object |
( |
Value
character
containing warning information or NULL
if no warnings.
Examples
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))
Separate calls
Description
Converts language object or lists of language objects to list of simple calls.
Usage
lang2calls(x)
Arguments
x |
|
Value
Given a call
, an expression
, a list of call
s or a list of expression
s, returns a list of calls
.
Symbols and atomic vectors (which may get mixed up in a list) are returned wrapped in list.
Examples
# use non-exported function from teal.code
lang2calls <- getFromNamespace("lang2calls", "teal.code")
expr <- expression(
i <- iris,
m <- mtcars
)
lang2calls(expr)
Instantiates a qenv
environment
Description
Instantiates a qenv
environment.
Usage
qenv()
Details
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 theeval_code()
andwithin.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
Value
qenv
environment.
See Also
eval_code()
, get_var()
, subset-qenv
, get_env()
,get_warnings()
, join()
, concat()
Examples
q <- qenv()
q2 <- within(q, a <- 1)
ls(q2)
q2$a
Reproducible class with environment and code
Description
Reproducible class with environment and code.
Slots
.xData
(
environment
) environment with content was generated by the evaluationcode
(
named list
ofcharacter
) representing code necessary to reproduce the environment. Read more in Code section. of thecode
slot.
Code
Each code element is a character representing one call. Each element is named with the random identifier to make sure uniqueness when joining. Each element has possible attributes:
-
warnings
(character
) the warnings output when evaluating the code element. -
messages
(character
) the messages output when evaluating the code element. -
dependency
(character
) names of objects that appear in this call and gets affected by this call, separated by<-
(objects on LHS of<-
are affected by this line, and objects on RHS are affecting this line).
Display qenv
object
Description
Prints the qenv
object.
Usage
## S4 method for signature 'qenv'
show(object)
Arguments
object |
( |
Value
object
, invisibly.
Examples
q <- qenv()
q1 <- eval_code(q, expression(a <- 5, b <- data.frame(x = 1:10)))
q1
Subsets qenv
Description
Subsets qenv
environment and limits the code to the necessary needed to build limited objects.
Usage
## S3 method for class 'qenv'
x[names, ...]
Arguments
x |
( |
names |
( |
... |
internal usage, please ignore. |
Examples
q <- qenv()
q <- eval_code(q, "a <- 1;b<-2")
q["a"]
q[c("a", "b")]