Skip to content

Commit d5d4b8d

Browse files
Merge pull request #533 from cynkra/b-526-fix-reticulate
fix environment issues re names and topenv
2 parents 6ebf781 + 498f8c5 commit d5d4b8d

8 files changed

+65
-13
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: constructive
22
Title: Display Idiomatic Code to Construct Most R Objects
3-
Version: 1.1.0
3+
Version: 1.1.0.9000
44
Authors@R: c(
55
person("Antoine", "Fabri", , "[email protected]", role = c("aut", "cre")),
66
person("Kirill", "Müller", , "[email protected]", role = "ctb",

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# constructive (development version)
2+
13
# constructive 1.1.0
24

35
Features:

R/contains_self_reference.R

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ contains_self_reference <- function(
1616
address <- rlang::obj_address(x)
1717
if (address %in% envs) return(TRUE)
1818
envs <- c(envs, address)
19+
# since we override S3 dispatch here we can circumvent rlang bug
20+
# https://github.com/r-lib/rlang/issues/1783
1921
bindings <- names(x)
20-
lazy_bindings <- bindings[rlang::env_binding_are_lazy(x)]
22+
lazy_bindings <- bindings[rlang::env_binding_are_lazy(x, bindings)]
2123
lazy_binding_envs <- lapply(lazy_bindings, promise_env, x)
2224
for (lazy_binding_env in lazy_binding_envs) {
2325
if (rec(lazy_binding_env)) return(TRUE)

R/environment_utils.R

+13-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ construct_special_env <- function(x) {
3030
}
3131
}
3232

33+
construct_top_env <- function(x) {
34+
repeat {
35+
code <- construct_special_env(x)
36+
if (!is.null(code)) break
37+
x <- parent.env(x)
38+
}
39+
code
40+
}
41+
3342
env_memory_address <- function(x, by_name = FALSE) {
3443
if (identical(Sys.getenv("TESTTHAT"), "true")) return("0x123456789")
3544
if (by_name) rlang::env_label(x) else rlang::obj_address(x)
@@ -159,7 +168,10 @@ update_predefinition <- function(envir, ...) {
159168
}
160169

161170
apply_env_locks <- function(x, code, ...) {
162-
locked_bindings <- rlang::env_binding_are_locked(x)
171+
# since we override S3 dispatch here we can circumvent rlang bug
172+
# https://github.com/r-lib/rlang/issues/1783
173+
bindings <- names(x)
174+
locked_bindings <- rlang::env_binding_are_locked(x, bindings)
163175
if (environmentIsLocked(x)) {
164176
if (length(locked_bindings) && all(locked_bindings)) {
165177
rhs <- c(

R/s3-environment.R

+21-5
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
#' its memory address.
4545
#' * `"list2env"`: We construct the environment as a list then
4646
#' use `base::list2env()` to convert it to an environment and assign it a parent. By
47-
#' default we will use `base::topenv()` to construct a parent. If `recurse` is `TRUE`
47+
#' default we use as a parent the first special environment we find when going
48+
#' through ancestors, so we can print code that doesn't use `.env()`.
49+
#' If `recurse` is `TRUE`
4850
#' the parent will be built recursively so all ancestors will be created until
4951
#' we meet a known environment, this might be verbose and will fail if environments
5052
#' are nested too deep or have a circular relationship. If the environment is empty we use `new.env(parent=)`
@@ -156,12 +158,19 @@ is_corrupted_environment <- function(x) {
156158
}
157159

158160
if (!opts$recurse) {
161+
parent_code <- construct_top_env(parent.env(x))
159162
if (length(names(x))) {
160-
code <- .cstr_apply(list(env2list(x), parent = topenv(x)), "list2env", ...)
163+
list_code <- .cstr_construct(env2list(x), ...)
164+
code <- .cstr_apply(
165+
list(list_code, parent = parent_code),
166+
"list2env",
167+
...,
168+
recurse = FALSE
169+
)
161170
code <- apply_env_locks(x, code, ...)
162171
return(repair_attributes_environment(x, code, ...))
163172
}
164-
code <- .cstr_apply(list(parent = topenv(x)), "new.env", ...)
173+
code <- .cstr_apply(list(parent = parent_code), "new.env", ..., recurse = FALSE)
165174
code <- apply_env_locks(x, code, ...)
166175
return(repair_attributes_environment(x, code, ...))
167176
}
@@ -195,12 +204,19 @@ is_corrupted_environment <- function(x) {
195204
abort_self_reference()
196205
}
197206
if (!opts$recurse) {
207+
parent_code <- construct_top_env(parent.env(x))
198208
if (length(names(x))) {
199-
code <- .cstr_apply(list(env2list(x), parent = topenv(x)), "rlang::new_environment", ...)
209+
list_code <- .cstr_construct(env2list(x), ...)
210+
code <- .cstr_apply(
211+
list(list_code, parent = parent_code),
212+
"rlang::new_environment",
213+
...,
214+
recurse = FALSE
215+
)
200216
code <- apply_env_locks(x, code)
201217
return(repair_attributes_environment(x, code, ...))
202218
}
203-
code <- .cstr_apply(list(parent = topenv(x)), "rlang::new_environment", ...)
219+
code <- .cstr_apply(list(parent = parent_code), "rlang::new_environment", ...)
204220
code <- apply_env_locks(x, code)
205221
return(repair_attributes_environment(x, code, ...))
206222
}

man/opts_environment.Rd

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/s3-environment.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,8 @@
2626
asNamespace("stats")
2727
Code
2828
construct(as.environment(head(cars, 2)), opts_environment("list2env"))
29-
Message
30-
{constructive} couldn't create code that reproduces perfectly the input
31-
i Call `construct_issues()` to inspect the last issues
3229
Output
33-
list2env(list(dist = c(2, 10), speed = c(4, 4)), parent = .GlobalEnv)
30+
list2env(list(dist = c(2, 10), speed = c(4, 4)), parent = emptyenv())
3431
Code
3532
construct(as.environment(head(cars, 2)), opts_environment(constructor = "new.env"))
3633
Message
@@ -213,3 +210,11 @@
213210
Output
214211
parent.env(asNamespace("stats"))
215212

213+
# environments with names method are constructed properly
214+
215+
Code
216+
construct(env, opts_environment("list2env"), check = FALSE)
217+
Output
218+
list2env(list(x = 1), parent = asNamespace("constructive")) |>
219+
structure(class = "foo")
220+

tests/testthat/test-s3-environment.R

+13
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,16 @@ test_that("environment", {
7171
construct(parent.env(asNamespace("stats")))
7272
})
7373
})
74+
75+
test_that("environments with names method are constructed properly", {
76+
env <- new.env()
77+
env$x <- 1
78+
class(env) <- "foo"
79+
names.foo <- function(x) "y"
80+
expect_snapshot({
81+
construct(env, opts_environment("list2env"), check = FALSE)
82+
})
83+
})
84+
85+
86+

0 commit comments

Comments
 (0)