Skip to content

Commit 03d952d

Browse files
m7prgithub-actions[bot]dependabot-preview[bot]
authored
210 bring get_code_dependency (#214)
BLOCKED by - insightsengineering/teal.data#340 Closes - #210 Companion to - insightsengineering/teal.data#343 - insightsengineering/teal#1388 # Description Brings `names` parameter to `get_code` so that you can limit returned code to specific objects (and the lines that create those objects). `get_code_dependency` was moved from `teal.data` # Tested with ``` r library(teal.code) # EVAL CODE q <- qenv() q <- eval_code(q, code = c("a <- 1", "b <- 2")) q@code #> [1] "a <- 1\nb <- 2" get_code(q, names = "a") #> [1] "a <- 1" # WITHIN q <- qenv() q <- within(q, {a <- 1; b <- 2}) q@code #> [1] "a <- 1\nb <- 2" get_code(q, names = "a") #> [1] "a <- 1" # OLD TEAL.DATA t <- teal.data::teal_data(a = 5, code = c("a <- 1", "b <- 2")) t@code #> [1] "a <- 1\nb <- 2" teal.data::get_code(t, datanames = 'a') #> Warning in .local(object, deparse, ...): get_code(datanames) was deprecated in #> teal.data 0.6.1, use get_code(names) instead. #> [1] "a <- 1" ``` <sup>Created on 2024-10-16 with [reprex v2.1.1](https://reprex.tidyverse.org)</sup> # Local tests ```r > devtools::test() ℹ Testing teal.code ✔ | F W S OK | Context ✔ | 12 | qenv_concat ✔ | 8 | qenv_constructor ✔ | 26 | qenv_eval_code ✔ | 2 60 | qenv_get_code [1.1s] ✔ | 10 | qenv_get_var ✔ | 7 | qenv_get_warnings ✔ | 40 | qenv_join ✔ | 14 | qenv_within ✔ | 12 | utils ══ Results ══════════════════════════════════════════════════════════════════ Duration: 2.2 s ── Skipped tests (2) ──────────────────────────────────────────────────────── • This is not urgent and can be ommitted with @linksto tag. (1): test-qenv_get_code.R:526:3 • We will not resolve this, as this requires code evaluation. (1): test-qenv_get_code.R:318:3 [ FAIL 0 | WARN 0 | SKIP 2 | PASS 189 ] ```` # Local R CMD CHECK ```r ── R CMD check results ──────────────────────────────────── teal.code 0.5.0.9010 ──── Duration: 33.5s ❯ checking for future file timestamps ... NOTE unable to verify current time 0 errors ✔ | 0 warnings ✔ | 1 note ✖ R CMD check succeeded ``` --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: 27856297+dependabot-preview[bot]@users.noreply.github.com <27856297+dependabot-preview[bot]@users.noreply.github.com>
1 parent 2c580e9 commit 03d952d

File tree

7 files changed

+1504
-7
lines changed

7 files changed

+1504
-7
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,5 @@ Collate:
6666
'qenv-show.R'
6767
'qenv-within.R'
6868
'teal.code-package.R'
69+
'utils-get_code_dependency.R'
6970
'utils.R'

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# teal.code 0.5.0.9010
22

3+
### Enhancements
4+
5+
* `get_code()` was extended with `names` parameter and allows the code extraction to be limited to objects stored in
6+
`qenv` but limited to `names`.
7+
38
# teal.code 0.5.0
49

510
### Breaking Change

R/qenv-get_code.R

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,100 @@
55
#'
66
#' @param object (`qenv`)
77
#' @param deparse (`logical(1)`) flag specifying whether to return code as `character` or `expression`.
8+
#' @param names `r lifecycle::badge("experimental")` (`character`) vector of object names to return the code for.
9+
#' For more details see the "Extracting dataset-specific code" section.
810
#' @param ... see `Details`
911
#'
12+
#'
13+
#' @section Extracting dataset-specific code:
14+
#' When `names` is specified, the code returned will be limited to the lines needed to _create_
15+
#' the requested objects. The code stored in the `@code` slot is analyzed statically to determine
16+
#' which lines the objects of interest depend upon. The analysis works well when objects are created
17+
#' with standard infix assignment operators (see `?assignOps`) but it can fail in some situations.
18+
#'
19+
#' Consider the following examples:
20+
#'
21+
#' _Case 1: Usual assignments._
22+
#' ```r
23+
#' q1 <- qenv() |>
24+
#' within({
25+
#' foo <- function(x) {
26+
#' x + 1
27+
#' }
28+
#' x <- 0
29+
#' y <- foo(x)
30+
#' })
31+
#' get_code(q1, names = "y")
32+
#' ```
33+
#' `x` has no dependencies, so `get_code(data, names = "x")` will return only the second call.\cr
34+
#' `y` depends on `x` and `foo`, so `get_code(data, names = "y")` will contain all three calls.
35+
#'
36+
#' _Case 2: Some objects are created by a function's side effects._
37+
#' ```r
38+
#' q2 <- qenv() |>
39+
#' within({
40+
#' foo <- function() {
41+
#' x <<- x + 1
42+
#' }
43+
#' x <- 0
44+
#' foo()
45+
#' y <- x
46+
#' })
47+
#' get_code(q2, names = "y")
48+
#' ```
49+
#' Here, `y` depends on `x` but `x` is modified by `foo` as a side effect (not by reassignment)
50+
#' and so `get_code(data, names = "y")` will not return the `foo()` call.\cr
51+
#' To overcome this limitation, code dependencies can be specified manually.
52+
#' Lines where side effects occur can be flagged by adding "`# @linksto <object name>`" at the end.\cr
53+
#' Note that `within` evaluates code passed to `expr` as is and comments are ignored.
54+
#' In order to include comments in code one must use the `eval_code` function instead.
55+
#'
56+
#' ```r
57+
#' q3 <- qenv() |>
58+
#' eval_code("
59+
#' foo <- function() {
60+
#' x <<- x + 1
61+
#' }
62+
#' x <- 0
63+
#' foo() # @linksto x
64+
#' y <- x
65+
#' ")
66+
#' get_code(q3, names = "y")
67+
#' ```
68+
#' Now the `foo()` call will be properly included in the code required to recreate `y`.
69+
#'
70+
#' Note that two functions that create objects as side effects, `assign` and `data`, are handled automatically.
71+
#'
72+
#' Here are known cases where manual tagging is necessary:
73+
#' - non-standard assignment operators, _e.g._ `%<>%`
74+
#' - objects used as conditions in `if` statements: `if (<condition>)`
75+
#' - objects used to iterate over in `for` loops: `for(i in <sequence>)`
76+
#' - creating and evaluating language objects, _e.g._ `eval(<call>)`
77+
#'
1078
#' @return
1179
#' `get_code` returns the traced code (from `@code` slot) in the form specified by `deparse`.
1280
#'
1381
#' @examples
1482
#' # retrieve code
83+
#' q <- within(qenv(), {
84+
#' a <- 1
85+
#' b <- 2
86+
#' })
1587
#' get_code(q)
1688
#' get_code(q, deparse = FALSE)
89+
#' get_code(q, names = "a")
90+
#'
91+
#' q <- qenv()
92+
#' q <- eval_code(q, code = c("a <- 1", "b <- 2"))
93+
#' get_code(q, names = "a")
1794
#'
1895
#' @name get_code
1996
#' @rdname qenv
2097
#' @aliases get_code,qenv-method
2198
#' @aliases get_code,qenv.error-method
2299
#'
23100
#' @export
24-
setGeneric("get_code", function(object, deparse = TRUE, ...) {
101+
setGeneric("get_code", function(object, deparse = TRUE, names = NULL, ...) {
25102
# this line forces evaluation of object before passing to the generic
26103
# needed for error handling to work properly
27104
grDevices::pdf(nullfile())
@@ -31,16 +108,29 @@ setGeneric("get_code", function(object, deparse = TRUE, ...) {
31108
standardGeneric("get_code")
32109
})
33110

34-
setMethod("get_code", signature = "qenv", function(object, deparse = TRUE) {
111+
setMethod("get_code", signature = "qenv", function(object, deparse = TRUE, names = NULL, ...) {
35112
checkmate::assert_flag(deparse)
113+
checkmate::assert_character(names, min.len = 1L, null.ok = TRUE)
114+
115+
# Normalize in case special it is backticked
116+
if (!is.null(names)) {
117+
names <- gsub("^`(.*)`$", "\\1", names)
118+
}
119+
120+
code <- if (!is.null(names)) {
121+
get_code_dependency(object@code, names, ...)
122+
} else {
123+
object@code
124+
}
125+
36126
if (deparse) {
37-
if (length(object@code) == 0) {
38-
object@code
127+
if (length(code) == 0) {
128+
code
39129
} else {
40-
paste(object@code, collapse = "\n")
130+
paste(code, collapse = "\n")
41131
}
42132
} else {
43-
parse(text = paste(c("{", object@code, "}"), collapse = "\n"), keep.source = TRUE)
133+
parse(text = paste(c("{", code, "}"), collapse = "\n"), keep.source = TRUE)
44134
}
45135
})
46136

0 commit comments

Comments
 (0)