gt icon indicating copy to clipboard operation
gt copied to clipboard

tidyselect usage broken when new empty environment is used.

Open cderv opened this issue 1 year ago • 2 comments

Comes from

  • https://github.com/rstudio/rmarkdown/issues/2537

following report at https://community.rstudio.com/t/rmd-rendering-on-connect-fail-while-knitting-locally-works/178673/16

Minimal reprex I get is

> evaluate::evaluate(c("library(gt)", "bob <- \"carb\"", "gt(mtcars) |>", "  cols_label(contains(bob)~bob)"), env = new.env(), stop_on_error = 2)
Error in `resolve_cols_i()`:
! Problem while evaluating `contains(bob)`.
Caused by error:
! objet 'bob' introuvable
Run `rlang::last_trace()` to see where the error occurred.
> rlang::last_trace()
<error/rlang_error>
Error in `resolve_cols_i()`:
! Problem while evaluating `contains(bob)`.
Caused by error:
! objet 'bob' introuvable
---
Backtrace:
     ▆
  1. ├─evaluate::evaluate(...)
  2. │ └─evaluate:::evaluate_call(...)
  3. │   ├─evaluate (local) timing_fn(...)
  4. │   ├─base (local) handle(...)
  5. │   ├─base::withCallingHandlers(...)
  6. │   ├─base::withVisible(...)
  7. │   └─evaluate:::eval_with_user_handlers(expr, envir, enclos, user_handlers)
  8. │     └─base::eval(expr, envir, enclos)
  9. │       └─base::eval(expr, envir, enclos)
 10. │         └─gt::cols_label(gt(mtcars), contains(bob) ~ bob)
 11. │           └─gt:::resolve_cols_c(expr = !!cols, data = .data)
 12. │             └─gt:::resolve_cols_i(...)
 13. │               ├─base::suppressWarnings(...)
 14. │               │ └─base::withCallingHandlers(...)
 15. │               └─tidyselect::eval_select(expr = quo, data = data, strict = strict)
 16. │                 └─tidyselect:::eval_select_impl(...)
 17. │                   ├─tidyselect:::with_subscript_errors(...)
 18. │                   │ └─rlang::try_fetch(...)
 19. │                   │   └─base::withCallingHandlers(...)
 20. │                   └─tidyselect:::vars_select_eval(...)
 21. │                     └─tidyselect:::walk_data_tree(expr, data_mask, context_mask)
 22. │                       └─tidyselect:::eval_context(expr, context_mask, call = error_call)
 23. │                         ├─tidyselect:::with_chained_errors(...)
 24. │                         │ └─rlang::try_fetch(...)
 25. │                         │   ├─base::tryCatch(...)
 26. │                         │   │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
 27. │                         │   │   └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
 28. │                         │   │     └─base (local) doTryCatch(return(expr), name, parentenv, handler)
 29. │                         │   └─base::withCallingHandlers(...)
 30. │                         └─rlang::eval_tidy(as_quosure(expr, env), context_mask)
 31. └─tidyselect::contains(bob)
 32.   └─tidyselect:::check_match(match)
 33.     └─rlang::is_character(match)
Run rlang::last_trace(drop = FALSE) to see 4 hidden frames.

Real use case within R Markdown and rendering with

rmarkdown::render("test.Rmd", envir = new.env())

which is what Posit connect do.

---
title: "testing"
output: html_document
---

```{r cars}
library(gt)
bob <- "carb"
gt(mtcars) |>
  cols_label(contains(bob)~bob)
```

cderv avatar Jan 19 '24 15:01 cderv

The example is indeed not great. This one throw similar but different error backtrace as it errors in f_rhs() so earlier

> evaluate::evaluate(c("library(gt)", "bob <- \"carb\"", "bob2 <- \"HELLO\"", "gt(mtcars) |>", "  cols_label(contains(bob)~bob2)"), env = new.env(), stop_on_error = 2)
Error:
! objet 'bob2' introuvable
Run `rlang::last_trace()` to see where the error occurred.
> rlang::last_trace()
<error/rlang_error>
Error:
! objet 'bob2' introuvable
---
Backtrace:
     ▆
  1. └─evaluate::evaluate(...)
  2.   └─evaluate:::evaluate_call(...)
  3.     ├─evaluate (local) timing_fn(...)
  4.     ├─base (local) handle(...)
  5.     ├─base::withCallingHandlers(...)
  6.     ├─base::withVisible(...)
  7.     └─evaluate:::eval_with_user_handlers(expr, envir, enclos, user_handlers)
  8.       └─base::eval(expr, envir, enclos)
  9.         └─base::eval(expr, envir, enclos)
 10.           └─gt::cols_label(gt(mtcars), contains(bob) ~ bob2)
 11.             └─rlang::eval_tidy(rlang::f_rhs(label_i))
---
title: "testing"
output: html_document
---

```{r cars}
library(gt)
bob <- "carb"
bob2 <- "new-carb"
gt(mtcars) |>
  cols_label(contains(bob)~bob2)
```

cderv avatar Jan 19 '24 16:01 cderv

I had @lionel- have a look into this, and here is the diagnosis he made:


it looks like formula parts are extracted without their environment and evaluated with eval_tidy(), for instance I see what boils down to:

f <- local({
    foo <- 1
    ~foo
})
library(rlang)
# Using `eval_tidy()` here does not help because the RHS is a bare expression
eval_tidy(f_rhs(f))
#> Error: objet 'foo' introuvable

Instead we need to extract the environment as well. The easiest way is to use as_quosure() which takes the RHS of a formula and makes it into a quosure where the expression is bundled with the environment:

f <- local({
    foo <- 1
    ~foo
})
library(rlang)
eval_tidy(as_quosure(f))
#> [1] 1

So @rich-iannone I believe we need to adapt some usage so that the publishing to connect issue does not happen anymore.

cderv avatar Feb 08 '24 17:02 cderv