Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change qenv as environment "type" -- adds names(qenv/qenv.error), get() and $ S3 methods #218

Merged
merged 58 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bfa8390
feat: initial support for names(teal_data)
averissimo Oct 28, 2024
127bc7c
docs: update NEWS
averissimo Oct 28, 2024
cb2cc57
docs: minor change
averissimo Oct 28, 2024
72f0595
fix: warning in R CMD check
averissimo Oct 28, 2024
9a6343a
docs: typo
averissimo Oct 28, 2024
e1a69b3
fix: remove implementation of names()<- as error message is self
averissimo Oct 28, 2024
c335f52
fix: remove extra arguments for names, not supported
averissimo Oct 28, 2024
873a1b6
fix: remove extra word from wordlist
averissimo Oct 28, 2024
a203f81
feat: `qenv` inherits from environment class
averissimo Oct 29, 2024
fd93704
Merge branch 'main' into 333_deprecate_datanames@main
averissimo Oct 29, 2024
8b855f3
[skip roxygen] [skip vbump] Roxygen Man Pages Auto Update
dependabot-preview[bot] Oct 29, 2024
4efca24
fix: improves tests and adds $ getter to qenv.error with similar beha…
averissimo Oct 29, 2024
0a02726
feat: prevent assignment to qenv.error
averissimo Oct 29, 2024
281c994
doc: update news
averissimo Oct 29, 2024
b1ffbe7
fix: problem when printing qenv.error
averissimo Oct 29, 2024
e457a4c
docs: adds .xData slot documentation
averissimo Oct 29, 2024
2c76a8a
chore: cleanup of previous implementation of names.qenv
averissimo Oct 29, 2024
ab9e342
chore: lintr cleanup
averissimo Oct 29, 2024
c56a5ca
feat: expand on compatibility with an environment
averissimo Oct 30, 2024
9904570
fix: complete tests for qenv-class
averissimo Oct 30, 2024
5c0ef7d
doc: adds section to qenv constructor
averissimo Oct 30, 2024
558bd72
fix: test and adds extra protection on qenv validation
averissimo Oct 30, 2024
4ec3e9f
fix: move constructor logic to "initialize" method of qenv
averissimo Oct 30, 2024
e00fd92
fix: problem with integer (1L) shorthand in within
averissimo Oct 30, 2024
be480f1
test: problem with integer (1L) shorthand in within
averissimo Oct 30, 2024
b7d1885
fix: order and formal of callNextMethod
averissimo Oct 30, 2024
2a32022
fix: minor bugs
averissimo Oct 30, 2024
9049379
chore: fix lintr
averissimo Oct 30, 2024
bb5c5fb
Apply suggestions from code review
averissimo Oct 31, 2024
709265f
docs: update
averissimo Oct 31, 2024
1fe8b18
docs: small improvements
averissimo Oct 31, 2024
3ae0541
[skip style] [skip vbump] Restyle files
github-actions[bot] Oct 31, 2024
0a64498
Update tests/testthat/test-qenv_within.R
averissimo Oct 31, 2024
d4ee6d0
docs: implements @gogonzo suggestions and cleans up docs
averissimo Oct 31, 2024
af7054b
docs: move section around
averissimo Oct 31, 2024
c668d98
docs: superseded
averissimo Oct 31, 2024
faa843b
fix: use newlines in code parseing on multiline expression with within
averissimo Nov 1, 2024
2f553eb
Merge branch 'main' into 333_deprecate_datanames@main
averissimo Nov 4, 2024
015f11c
fix: problems with check
averissimo Nov 4, 2024
7cd8949
chore: rename instances of ls to names
averissimo Nov 5, 2024
50be4b0
chore: rename instances of join to c
averissimo Nov 5, 2024
9d2ec00
docs: improvement on join() documentation
averissimo Nov 5, 2024
c76e148
Apply suggestions from code review
averissimo Nov 5, 2024
620849b
pr: apply suggestions
averissimo Nov 5, 2024
1e25681
pr: apply suggestions (remove duplicate test)
averissimo Nov 5, 2024
7b7ae6a
Update R/qenv-join.R
averissimo Nov 5, 2024
7ea19e3
fix: error with suggestion
averissimo Nov 5, 2024
bf5ed47
fix: tests
averissimo Nov 5, 2024
903a43c
feat: qenv constructor improvement
averissimo Nov 6, 2024
f9fef18
Update tests/testthat/test-qenv-class.R
averissimo Nov 7, 2024
3bd3ff5
[skip style] [skip vbump] Restyle files
github-actions[bot] Nov 7, 2024
d8d1a8e
chore: trigger CI
averissimo Nov 7, 2024
236ce59
Update README.md
averissimo Nov 7, 2024
4564f34
Update R/qenv-get_env.R
averissimo Nov 7, 2024
9f76e48
[skip roxygen] [skip vbump] Roxygen Man Pages Auto Update
dependabot-preview[bot] Nov 7, 2024
49e49c9
fix: remove unnecessary listing
averissimo Nov 7, 2024
1f4b755
[skip roxygen] [skip vbump] Roxygen Man Pages Auto Update
dependabot-preview[bot] Nov 7, 2024
a5fcb9a
chore: trigger CI
averissimo Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Generated by roxygen2: do not edit by hand

S3method("$",qenv.error)
S3method("$<-",qenv.error)
S3method("[[",qenv.error)
S3method("[[<-",qenv.error)
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
S3method(within,qenv)
S3method(within,qenv.error)
export(concat)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* `get_code()` was extended with `names` parameter and allows the code extraction to be limited to objects stored in
`qenv` but limited to `names`.
* Added `names()` function for `qenv` objects, which dynamically determines the visible objects in the environment.

# teal.code 0.5.0

Expand Down
15 changes: 12 additions & 3 deletions R/qenv-class.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@
#' @exportClass qenv
setClass(
"qenv",
slots = c(env = "environment", code = "character", id = "integer", warnings = "character", messages = "character"),
slots = c(
code = "character",
id = "integer",
warnings = "character",
messages = "character"
),
contains = "environment",
prototype = list(
env = new.env(parent = parent.env(.GlobalEnv)), code = character(0), id = integer(0),
warnings = character(0), messages = character(0)
.xData = new.env(parent = parent.env(.GlobalEnv)),
code = character(0),
id = integer(0),
warnings = character(0),
messages = character(0)
)
)

Expand Down
4 changes: 2 additions & 2 deletions R/qenv-concat.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ setMethod("concat", signature = c("qenv", "qenv"), function(x, y) {
y@messages <- c(x@messages, y@messages)

# insert (and overwrite) objects from y to x
y@env <- rlang::env_clone(y@env, parent = parent.env(.GlobalEnv))
rlang::env_coalesce(env = y@env, from = x@env)
y@.xData <- rlang::env_clone(y@.xData, parent = parent.env(.GlobalEnv))
rlang::env_coalesce(env = y@.xData, from = x@.xData)
y
})

Expand Down
8 changes: 6 additions & 2 deletions R/qenv-constructor.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
qenv <- function() {
q_env <- new.env(parent = parent.env(.GlobalEnv))
lockEnvironment(q_env, bindings = TRUE)
methods::new("qenv", env = q_env)
methods::new("qenv", .xData = q_env)
}


Expand Down Expand Up @@ -73,7 +73,11 @@ setMethod(
id <- sample.int(.Machine$integer.max, size = length(code))
methods::new(
"qenv",
env = new_env, code = code, warnings = rep("", length(code)), messages = rep("", length(code)), id = id
.xData = new_env,
code = code,
warnings = rep("", length(code)),
messages = rep("", length(code)),
id = id
)
}
)
Expand Down
11 changes: 5 additions & 6 deletions R/qenv-eval_code.R
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
id <- sample.int(.Machine$integer.max, size = 1)

object@id <- c(object@id, id)
object@env <- rlang::env_clone(object@env, parent = parent.env(.GlobalEnv))
object@.xData <- rlang::env_clone(object@.xData, parent = parent.env(.GlobalEnv))
averissimo marked this conversation as resolved.
Show resolved Hide resolved
code <- paste(code, collapse = "\n")
object@code <- c(object@code, code)

Expand All @@ -45,11 +45,11 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
x <- withCallingHandlers(
tryCatch(
{
eval(single_call, envir = object@env)
if (!identical(parent.env(object@env), parent.env(.GlobalEnv))) {
eval(single_call, envir = object@.xData)
if (!identical(parent.env(object@.xData), parent.env(.GlobalEnv))) {
# needed to make sure that @env is always a sibling of .GlobalEnv
# could be changed when any new package is added to search path (through library or require call)
parent.env(object@env) <- parent.env(.GlobalEnv)
parent.env(object@.xData) <- parent.env(.GlobalEnv)
}
NULL
},
Expand Down Expand Up @@ -80,11 +80,10 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
}
}


object@warnings <- c(object@warnings, current_warnings)
object@messages <- c(object@messages, current_messages)

lockEnvironment(object@env, bindings = TRUE)
lockEnvironment(object@.xData, bindings = TRUE)
object
})

Expand Down
2 changes: 1 addition & 1 deletion R/qenv-get_env.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ setGeneric("get_env", function(object) {
})

setMethod("get_env", "qenv", function(object) {
object@env
object@.xData
})

setMethod("get_env", "qenv.error", function(object) {
Expand Down
28 changes: 27 additions & 1 deletion R/qenv-get_var.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ setGeneric("get_var", function(object, var) {

setMethod("get_var", signature = c("qenv", "character"), function(object, var) {
tryCatch(
get(var, envir = object@env, inherits = FALSE),
get(var, envir = object@.xData, inherits = FALSE),
averissimo marked this conversation as resolved.
Show resolved Hide resolved
error = function(e) {
message(conditionMessage(e))
NULL
Expand All @@ -55,3 +55,29 @@ setMethod("[[", signature = c("qenv", "ANY"), function(x, i) {
class = c("validation", "try-error", "simpleError")
))
}

#' @export
`$.qenv.error` <- function(x, name) {
averissimo marked this conversation as resolved.
Show resolved Hide resolved
# Must allow access of elements in qenv.error object (message, call, trace, ...)
# Otherwise, it will enter an infinite recursion with the `conditionMessage(x)` call.
result <- NextMethod("$", x)
if (is.null(result)) {
class(x) <- setdiff(class(x), "qenv.error")
stop(errorCondition(
list(message = conditionMessage(x)),
class = c("validation", "try-error", "simpleError")
))
}
result
}

#' @export
`[[<-.qenv.error` <- function(x, name, value) {
stop(errorCondition(
list(message = conditionMessage(x)),
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
class = c("validation", "try-error", "simpleError")
))
}

#' @export
`$<-.qenv.error` <- `[[<-.qenv.error`
8 changes: 4 additions & 4 deletions R/qenv-join.R
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ setMethod("join", signature = c("qenv", "qenv"), function(x, y) {
x@messages <- c(x@messages, y@messages[id_unique])

# insert (and overwrite) objects from y to x
x@env <- rlang::env_clone(x@env, parent = parent.env(.GlobalEnv))
rlang::env_coalesce(env = x@env, from = y@env)
x@.xData <- rlang::env_clone(x@.xData, parent = parent.env(.GlobalEnv))
rlang::env_coalesce(env = x@.xData, from = y@.xData)
x
})

Expand All @@ -175,9 +175,9 @@ setMethod("join", signature = c("qenv.error", "ANY"), function(x, y) {
checkmate::assert_class(x, "qenv")
checkmate::assert_class(y, "qenv")

common_names <- intersect(rlang::env_names(x@env), rlang::env_names(y@env))
common_names <- intersect(rlang::env_names(x@.xData), rlang::env_names(y@.xData))
is_overwritten <- vapply(common_names, function(el) {
!identical(get(el, x@env), get(el, y@env))
!identical(get(el, x@.xData), get(el, y@.xData))
}, logical(1))
if (any(is_overwritten)) {
return(
Expand Down
2 changes: 1 addition & 1 deletion R/qenv-show.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
#' @importFrom methods show
#' @export
setMethod("show", "qenv", function(object) {
rlang::env_print(object@env)
rlang::env_print(object@.xData)
})
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ reference:
- get_var
- get_warnings
- join
- names.qenv
- new_qenv
- qenv
- show,qenv-method
Expand Down
4 changes: 2 additions & 2 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Forkers
Hoffmann
Reproducibility
funder
Hoffmann
qenv
repo
Reproducibility
reproducibility
4 changes: 2 additions & 2 deletions tests/testthat/test-qenv_concat.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ testthat::test_that("Concatenate two identical qenvs outputs", {

q12 <- concat(q1, q2)

testthat::expect_equal(q12@env, q1@env)
testthat::expect_equal(q12@.xData, q1@.xData)
testthat::expect_identical(
q12@code,
c("iris1 <- iris", "iris1 <- iris")
Expand All @@ -21,7 +21,7 @@ testthat::test_that("Concatenate two independent qenvs results in object having

q12 <- concat(q1, q2)

testthat::expect_equal(q12@env, list2env(list(iris1 = iris, mtcars1 = mtcars)))
testthat::expect_equal(q12@.xData, list2env(list(iris1 = iris, mtcars1 = mtcars)))
testthat::expect_identical(
q12@code,
c("iris1 <- iris", "mtcars1 <- mtcars")
Expand Down
60 changes: 54 additions & 6 deletions tests/testthat/test-qenv_constructor.R
Original file line number Diff line number Diff line change
@@ -1,19 +1,67 @@
testthat::describe("qenv inherits from environment: ", {
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
testthat::it("is an environment", {
testthat::expect_true(is.environment(qenv()))
})

testthat::it("ls() shows nothing on empty environment", {
testthat::expect_identical(ls(qenv(), all.names = TRUE), character(0))
})

testthat::it("ls() shows available objets", {
q <- within(qenv(), iris <- iris)
testthat::expect_setequal(ls(q), "iris")
})

testthat::it("ls() does not show hidden objects", {
q <- within(qenv(), {
iris <- iris
.hidden <- 2
})
testthat::expect_setequal(ls(q), "iris")
})

testthat::it("names() show all objects", {
q <- eval_code(qenv(), "
iris <- iris
.hidden <- 2
")
testthat::expect_setequal(names(q), c("iris", ".hidden"))
})

testthat::it("does not allow binding to be added", {
q <- qenv()
testthat::expect_error(q$x <- 1, "cannot add bindings to a locked environment")
})

testthat::it("does not allow binding to be modified", {
q <- within(qenv(), obj <- 1)
testthat::expect_error(q$obj <- 2, "cannot change value of locked binding for 'obj'")
})
})

testthat::test_that("constructor returns qenv", {
q <- qenv()
testthat::expect_s4_class(q, "qenv")
testthat::expect_identical(ls(q@env), character(0))
testthat::expect_identical(ls(q@.xData, all.names = TRUE), character(0))
testthat::expect_identical(q@code, character(0))
testthat::expect_identical(q@id, integer(0))
testthat::expect_identical(q@warnings, character(0))
testthat::expect_identical(q@messages, character(0))
})

testthat::test_that("parent of qenv environment is the parent of .GlobalEnv", {
q <- qenv()
testthat::expect_identical(parent.env(q@env), parent.env(.GlobalEnv))
testthat::describe("parent of qenv environment is the parent of .GlobalEnv", {
testthat::it("via slot", {
q <- qenv()
testthat::expect_identical(parent.env([email protected]), parent.env(.GlobalEnv))
})

testthat::it("via qenv directly", {
q <- qenv()
testthat::expect_identical(parent.env(q), parent.env(.GlobalEnv))
})
})

testthat::test_that("parent of qenv environment is locked", {
testthat::test_that("qenv environment is locked", {
q <- qenv()
testthat::expect_error(q@env$x <- 1, "cannot add bindings to a locked environment")
testthat::expect_error(q@.xData$x <- 1, "cannot add bindings to a locked environment")
})
averissimo marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 10 additions & 9 deletions tests/testthat/test-qenv_eval_code.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
)
})

testthat::test_that("@env in qenv is always a sibling of .GlobalEnv", {
testthat::test_that("@.xData in qenv is always a sibling of .GlobalEnv", {
q1 <- qenv()
testthat::expect_identical(parent.env(q1@env), parent.env(.GlobalEnv))
testthat::expect_identical(parent.env(q1@.xData), parent.env(.GlobalEnv))

q2 <- eval_code(q1, quote(a <- 1L))
testthat::expect_identical(parent.env(q2@env), parent.env(.GlobalEnv))
testthat::expect_identical(parent.env(q2@.xData), parent.env(.GlobalEnv))
q3 <- eval_code(q2, quote(b <- 2L))
testthat::expect_identical(parent.env(q3@env), parent.env(.GlobalEnv))
testthat::expect_identical(parent.env(q3@.xData), parent.env(.GlobalEnv))
})
averissimo marked this conversation as resolved.
Show resolved Hide resolved

testthat::test_that("getting object from the package namespace works even if library in the same call", {
Expand All @@ -42,21 +42,21 @@
q1 <- eval_code(qenv(), "a <- 1")

testthat::expect_identical(q1@code, "a <- 1")
testthat::expect_equal(q1@env, list2env(list(a = 1)))
testthat::expect_equal(q1@.xData, list2env(list(a = 1)))
})

testthat::test_that("eval_code works with expression", {
q1 <- eval_code(qenv(), as.expression(quote(a <- 1)))

testthat::expect_identical(q1@code, "a <- 1")
testthat::expect_equal(q1@env, list2env(list(a = 1)))
testthat::expect_equal(q1@.xData, list2env(list(a = 1)))
})

testthat::test_that("eval_code works with quoted", {
q1 <- eval_code(qenv(), quote(a <- 1))

testthat::expect_identical(q1@code, "a <- 1")
testthat::expect_equal(q1@env, list2env(list(a = 1)))
testthat::expect_equal(q1@.xData, list2env(list(a = 1)))
})

testthat::test_that("eval_code works with quoted code block", {
Expand All @@ -72,11 +72,12 @@
q1@code,
"a <- 1\nb <- 2"
)
testthat::expect_equal(q1@env, list2env(list(a = 1, b = 2)))
testthat::expect_equal(q1@.xData, list2env(list(a = 1, b = 2)))
})

testthat::test_that("eval_code fails with unquoted expression", {
testthat::expect_error(eval_code(qenv(), a <- b), "object 'b' not found")
b <- 3
testthat::expect_error(eval_code(qenv(), a <- b), "unable to find an inherited method for function .eval_code. for signature")

Check warning on line 80 in tests/testthat/test-qenv_eval_code.R

View workflow job for this annotation

GitHub Actions / SuperLinter 🦸‍♀️ / Lint R code 🧶

file=tests/testthat/test-qenv_eval_code.R,line=80,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 128 characters.
})

testthat::test_that("an error when calling eval_code returns a qenv.error object which has message and trace", {
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-qenv_get_code.R
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ testthat::test_that("extracts the code without downstream usage", {
)
})

testthat::test_that("works for datanames of length > 1", {
testthat::test_that("works for names of length > 1", {
code <- c(
"a <- 1",
"b <- 2"
Expand Down
Loading
Loading