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

evaluation of chunk option and working directory ? #2081

Closed
cderv opened this issue Dec 8, 2021 · 2 comments
Closed

evaluation of chunk option and working directory ? #2081

cderv opened this issue Dec 8, 2021 · 2 comments
Labels
next Issues/PRs considered for the next release

Comments

@cderv
Copy link
Collaborator

cderv commented Dec 8, 2021

With the new file option (545aed3), there is now a way to include external document for chunk content.

If this is useful, it could become tricky when manipulating working directory. Currently, chunk options are evaluated without taking into account the root.dir, especially file and code

knitr/R/block.R

Lines 36 to 40 in 545aed3

params[["code"]] = if (is.null(code_file <- params[['file']])) {
params[["code"]] %n% unlist(knit_code$get(ref.label), use.names = FALSE)
} else {
# TODO: use xfun::read_all() so we can read multiple files at once
xfun::read_utf8(code_file)

input_dir() is not used

knitr/R/utils.R

Lines 153 to 156 in faa5093

input_dir = function() {
root = opts_knit$get('root.dir')
root %n% (if (!getOption('knitr.use.cwd', FALSE)) .knitEnv$input.dir) %n% '.'
}

and currently chunk option are evaluated from the working dir set when knitr::knit is run. (so not even the input file directory )

Is a possible fix as simple as

diff --git a/R/block.R b/R/block.R
index 6eba1996..256465ba 100644
--- a/R/block.R
+++ b/R/block.R
@@ -37,7 +37,7 @@ call_block = function(block) {
     params[["code"]] %n% unlist(knit_code$get(ref.label), use.names = FALSE)
   } else {
     # TODO: use xfun::read_all() so we can read multiple files at once
-    xfun::read_utf8(code_file)
+    xfun::in_dir(input_dir(), xfun::read_utf8(code_file))
   }

   # opts.label = TRUE means inheriting chunk options

I think this is important because currently it is a bit counter intuitive to deal with two different working dir which will impact the way relative file path are evaluated in chunks and in chunk option. It was mainly an issue when code option was used with a read function, and will now be more frequent probably with the file option.

Maybe a larger fix is needed for all chunk options - I did not test it yet with other option like fig.path - maybe this needs a fix only for code and file chunk option. For now, only child option has been fixed regarding this issue: #2059

Example code illustrating this issue

Setting up a dummy project

dir.create(tmp_dir <- tempfile())
old <- setwd(tmp_dir)
fs::dir_create("dummy")
fs::dir_create("dummy/rmd")
xfun::write_utf8("1+1", "dummy/content.R")
root <- fs::path(tmp_dir, "dummy")
rmd_file <- "dummy/rmd/test.Rmd"

A working file

xfun::write_utf8(glue::glue("
---
title: test
output: html_document
---

```{r}
knitr::opts_knit$set(root.dir = '<root>')
```

```{r}
getwd()
fs::dir_ls()
```

```{r using-file, file = 'dummy/content.R'}
```
", .open = "<", .close = ">"), rmd_file)

We have this

fs::dir_ls(recurse = TRUE)
#> dummy              dummy/content.R    dummy/rmd          dummy/rmd/test.Rmd test.md
xfun::file_string(rmd_file)
#> ---
#> title: test
#> output: html_document
#> ---
#> 
#> ```{r}
#> knitr::opts_knit$set(root.dir = 'C:/Users/chris/AppData/Local/Temp/RtmpI53qI2/file42ac2a5f7ddb/dummy')
#> ```
#> 
#> ```{r}
#> getwd()
#> fs::dir_ls()
#> ```
#> 
#> ```{r using-file, file = 'dummy/content.R'}
#> ```
knitr::knit("dummy/rmd/test.Rmd")
#> processing file: dummy/rmd/test.Rmd
#> output file: test.md
#> [1] "test.md"
xfun::file_string("test.md")
#> ---
#> title: test
#> output: html_document
#> ---
#> 
#> 
#> ```r
#> knitr::opts_knit$set(root.dir = 'C:/Users/chris/AppData/Local/Temp/RtmpI53qI2/file42ac2a5f7ddb/dummy')
#> ```
#> 
#> 
#> ```r
#> getwd()
#> #> [1] "C:/Users/chris/AppData/Local/Temp/RtmpI53qI2/file42ac2a5f7ddb/dummy"
#> fs::dir_ls()
#> #> content.R rmd
#> ```
#> 
#> 
#> ```r
#> 1+1
#> #> [1] 2
#> ```

For it to work, I needed to pass to file a relative path to R working dir of the session and not root.dir.

But this would not work if I were to use rmarkdown::render(), because render() will change the working dir before knit() to be the directory where the input file is

> res <- rmarkdown::render(rmd_file, run_pandoc = FALSE, quiet = TRUE)
Quitting from lines 16-16 (test.Rmd) 
Error in file(con, "r") : cannot open the connection
In addition: Warning message:
In file(con, "r") :
  cannot open file 'dummy/content.R': No such file or directory

I would need to pass a relative path to the input file

---
title: test
output: html_document
---

```{r}
knitr::opts_knit$set(root.dir = 'C:/Users/chris/AppData/Local/Temp/RtmpI53qI2/file42ac2a5f7ddb/dummy')
```

```{r}
getwd()
fs::dir_ls()
```

```{r using-file, file = '../content.R'}
```

What would be expected is to pass a relative path compared to root.dir

> xfun::file_string(rmd_file)
---
title: test
output: html_document
---

```{r}
knitr::opts_knit$set(root.dir = 'C:/Users/chris/AppData/Local/Temp/RtmpI53qI2/file42ac2a5f7ddb/dummy')
```

```{r}
getwd()
fs::dir_ls()
```

```{r using-file, file = 'content.R'}
```
> knitr::knit("dummy/rmd/test.Rmd")


processing file: dummy/rmd/test.Rmd
  |.................                                                                                      |  17%
  ordinary text without R code

  |..................................                                                                     |  33%
label: unnamed-chunk-1
  |....................................................                                                   |  50%
  ordinary text without R code

  |.....................................................................                                  |  67%
label: unnamed-chunk-2
  |......................................................................................                 |  83%
  ordinary text without R code

  |.......................................................................................................| 100%
Quitting from lines 16-16 (dummy/rmd/test.Rmd) 
Error in file(con, "r") : cannot open the connection
In addition: Warning message:
In file(con, "r") : cannot open file 'content.R': No such file or directory

but that does not work.

# cleaning
setwd(old)
unlink(tmp_dir, recursive = TRUE)

So the "mental model" of how working directory works with knitr and rmarkdown is not easy - also because usually you expect one working directory only for all R code.

I believe there is something to discuss and fix here.

cc @jennybc

@yihui yihui moved this to In Progress in R Markdown Team Projects Mar 24, 2022
@yihui yihui closed this as completed in a3db3c7 Mar 24, 2022
Repository owner moved this from In Progress to Done in R Markdown Team Projects Mar 24, 2022
@yihui yihui added the next Issues/PRs considered for the next release label Mar 24, 2022
@yihui
Copy link
Owner

yihui commented Mar 24, 2022

Yes, I think it makes sense to use input_dir() to evaluate chunk options. Done now. Not sure if it will affect any reverse dependencies. I'll see. Thanks!

@github-actions
Copy link

This old thread has been automatically locked. If you think you have found something related to this, please open a new issue by following the issue guide (https://yihui.org/issue/), and link to this old issue if necessary.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
next Issues/PRs considered for the next release
Projects
None yet
Development

No branches or pull requests

2 participants