Code tracking with qenv
object
Source: R/qenv-constructor.R
, R/qenv-eval_code.R
, R/qenv-extract.R
, and 2 more
qenv.Rd
Usage
qenv()
eval_code(object, code)
# S3 method for class 'qenv'
x[names, ...]
get_code(object, deparse = TRUE, names = NULL, ...)
# S3 method for class 'qenv'
within(data, expr, ...)
Arguments
- object
(
qenv
)- code
(
character
,language
orexpression
) code to evaluate. It is possible to preserve original formatting of thecode
by providing acharacter
or anexpression
being a result ofparse(keep.source = TRUE)
.- x
(
qenv
)- names
(
character
) forx[names]
, names of objects included inqenv
to subset. Names not present inqenv
are skipped. Forget_code
vector of object names to return the code for. For more details see the "Extracting dataset-specific code" section.- ...
see
Details
- deparse
(
logical(1)
) flag specifying whether to return code ascharacter
orexpression
.- data
(
qenv
)- expr
(
expression
) to evaluate. Must be inline code, seeUsing language objects...
Value
qenv
returns a qenv
object.
eval_code
returns a qenv
object with expr
evaluated or qenv.error
if evaluation fails.
[[
, $
and get
return the value of the object named name
in the qenv
object.
names
return a character vector of all the names of the objects in the qenv
object.
ls
return a character vector of the names of the objects in the qenv
object.
It will only show the objects that are not named with a dot prefix, unless
the all.names = TRUE
, which will show all objects.
get_code
returns the traced code in the form specified by deparse
.
within
returns a qenv
object with expr
evaluated or qenv.error
if evaluation fails.
Details
qenv()
instantiates a qenv
with an empty environment.
Any changes must be made by evaluating code in it with eval_code
or within
, thereby ensuring reproducibility.
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.
x[[name]]
, x$name
and get(name, x)
are generic R operators to access the objects in the environment.
See [[[
] for more details.
names(x)
calls on the qenv
object and will list all objects in the environment.
get_code()
retrieves the code stored in the qenv
. ...
passes arguments to methods.
within()
is a convenience function for evaluating inline code inside the environment of a qenv
.
It is a method for the base
generic that wraps eval_code
to provide a simplified way of passing code.
within
accepts only inline expressions (both simple and compound) and allows for injecting values into expr
through the ...
argument:
as name:value
pairs are passed to ...
, name
in expr
will be replaced with value
.
Subsetting
x[names]
subsets objects in qenv
environment and limit the code to the necessary needed to build limited objects.
...
passes parameters to further methods.
Extracting dataset-specific code
When names
for get_code
is specified, the code returned will be limited to the 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.
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>)
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
# create empty qenv
qenv()
#> <environment: 0x55f292595678> [L]
#> Parent: <environment: devtools_shims>
# 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)))
# Subsetting
q <- qenv()
q <- eval_code(q, "a <- 1;b<-2")
q["a"]
#> <environment: 0x55f293bdae50> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • a: <dbl>
q[c("a", "b")]
#> <environment: 0x55f28f10bad0> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • b: <dbl>
#> • a: <dbl>
# Extract objects from qenv
q[["a"]]
#> [1] 1
q$a
#> [1] 1
# list objects in qenv
names(q)
#> [1] "a" "b"
# retrieve code
q <- within(qenv(), {
a <- 1
b <- 2
})
get_code(q)
#> [1] "a <- 1\nb <- 2"
get_code(q, deparse = FALSE)
#> expression({
#> a <- 1
#> b <- 2
#> })
get_code(q, names = "a")
#> [1] "a <- 1"
q <- qenv()
q <- eval_code(q, code = c("a <- 1", "b <- 2"))
get_code(q, names = "a")
#> [1] "a <- 1"
# evaluate code using within
q <- qenv()
q <- within(q, {
i <- iris
})
q <- within(q, {
m <- mtcars
f <- faithful
})
q
#> <environment: 0x55f2936133c8> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • f: <df[,2]> [L]
#> • i: <df[,5]> [L]
#> • m: <df[,11]> [L]
get_code(q)
#> [1] "i <- iris\nm <- mtcars\nf <- faithful"
# inject values into code
q <- qenv()
q <- within(q, i <- iris)
within(q, print(dim(subset(i, Species == "virginica"))))
#> [1] 50 5
#> <environment: 0x55f29400d918> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • i: <df[,5]> [L]
within(q, print(dim(subset(i, Species == species)))) # fails
#> <qenv.error: object 'species' not found
#> when evaluating qenv code:
#> print(dim(subset(i, Species == species)))>
within(q, print(dim(subset(i, Species == species))), species = "versicolor")
#> [1] 50 5
#> <environment: 0x55f28f696a08> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • i: <df[,5]> [L]
species_external <- "versicolor"
within(q, print(dim(subset(i, Species == species))), species = species_external)
#> [1] 50 5
#> <environment: 0x55f290b59b48> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • i: <df[,5]> [L]
# pass language objects
expr <- expression(i <- iris, m <- mtcars)
within(q, expr) # fails
#> <qenv.error: object 'expr' not found
#> when evaluating qenv code:
#> expr>
do.call(within, list(q, expr))
#> <environment: 0x55f292be0e38> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • i: <df[,5]> [L]
#> • m: <df[,11]> [L]
exprlist <- list(expression(i <- iris), expression(m <- mtcars))
within(q, exprlist) # fails
#> <qenv.error: object 'exprlist' not found
#> when evaluating qenv code:
#> exprlist>
do.call(within, list(q, do.call(c, exprlist)))
#> <environment: 0x55f29359dea8> [L]
#> Parent: <environment: package:checkmate>
#> Bindings:
#> • i: <df[,5]> [L]
#> • m: <df[,11]> [L]