0

When building an R Markdown website with rmarkdown::render_site(), it seems that each of the knitted files all share the same environment and knitr/R Markdown does not create a new empty environment for each individual page. This causes unexpected namespace issues that I can't figure out how to get rid of.

For example, take this minimum working example with the following 5 files:

testing.Rproj

Version: 1.0

RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default

EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8

RnwWeave: Sweave
LaTeX: pdfLaTeX

AutoAppendNewline: Yes

BuildType: Website

_site.yml

name: "testing"
navbar:
  title: "Testing"
  left:
    - text: "Home"
      href: index.html
    - text: "Step 1"
      href: 01_something.html
    - text: "Step 2"
      href: 02_something-else.html

index.Rmd

---
title: "Testing"
---

Hello, Website!

01_something.Rmd

---
title: "Step 1"
---

Write some data just for fun.

```{r}
library(tidyverse)
library(here)

write_csv(mtcars, file.path(here(), "cars.csv"))
```

02_something-else.Rmd

---
title: "Step 2"
---

This breaks because `lubridate::here()` and `here::here()` conflict, but *only* when rendering the whole site.

```{r}
library(tidyverse)
library(lubridate)
library(here)

# Do something with lubridate
my_date <- ymd("2018-04-19")

# Try to use here() and it breaks when rendering the whole site
# It works just fine when knitting this file on its own, though, since here is loaded after lubridate
cars <- read_csv(file.path(here(), "cars.csv"))
```

Both here and lubridate have a here() function, and because I want to use lubridate's throughout the script in 02_something-else.Rmd, I run library(here) after library(lubridate). Running 02_something-else.Rmd interactively or knitting it by itself works just fine—package loading happens in the right order and everything's great.

However, when building the site with rmarkdown::render_site() (from the console, from the "Build" button in RStudio, or from the terminal with Rscript -e "rmarkdown::render_site())") there's an error when R gets to 02_something-else.Rmd:

Error: '2018-04-19 14:53:59/cars.csv' does not exist in current working directory

Instead of using here::here(), R is using lubridate::here() and inserting the current date and time, since library(here) was originally loaded in 01_something.Rmd and that environment seems to still be loaded when R gets to 02_something-else.Rmd.

According to the documentation for rmarkdown::render_site(), you can use the envir = new.env() argument to ensure that the site rendering uses a new environment, but that doesn't fix the problem. It seems that guarantees a new environment for the overall site building process, but not for the individual files.

Is there a way to ensure that each individual file in an R Markdown website gets its own new environment when knitted?

Andrew
  • 36,541
  • 13
  • 67
  • 93
  • I could just use `here::here()` throughout, but that doesn't fix the underlying problem. This can get more complicated with other conflicts too, like `dplyr::filter()` and `MASS::filter()`—if I use MASS in one file in the site, it would mess up code in all the files that come after, but I wouldn't realize it until I build the whole website. – Andrew Apr 19 '18 at 21:18
  • There is some code here to clear all your loaded packages if that helps: https://stackoverflow.com/questions/7505547/detach-all-packages-while-working-in-r – Ian Wesley Apr 19 '18 at 21:44
  • Ah cool, that'll work. But it still seems wrong to have to do that at the beginning of each Rmd file, since the theory behind knitting all these files is that they're reproducible on their own. – Andrew Apr 19 '18 at 21:50
  • Especially since some (like Jenny Bryan) are morally opposed to `rm(list = ls())`: https://www.tidyverse.org/articles/2017/12/workflow-vs-script/ – Andrew Apr 19 '18 at 21:52
  • Update: This is just how R Markdown websites work: https://github.com/rstudio/rmarkdown/issues/1326#issuecomment-382957907 – Andrew Apr 20 '18 at 03:18
  • 1
    A quibble about jargon: the problem isn't that `here` is loaded, the problem is that it's still "attached", i.e. on the search list. You can use `detach("package:here")` to remove it. It's probably worth writing a function to clean up your search list at the start of each new document, given this flaw in `render_site()`. – user2554330 Apr 20 '18 at 22:47

1 Answers1

1

This looks like a flaw in the design of rmarkdown::render_site, but it's easy to work around. The problem isn't that the here package is loaded, the problem is that it's on the search list. So you should remove it. (Removing things from the search list is easier than unloading them, and is generally pretty safe.)

It seems like a good defensive measure would be to clean up your search list at the start of each document. This function does that:

cleanSearch <- function() {
    defaults <- c(".GlobalEnv", 
                  paste0("package:", getOption("defaultPackages")),
                  "Autoloads",
                  "package:base")
    currentList <- search()  
    deletes <- setdiff(currentList, defaults)
    for (entry in deletes)
      detach(entry, character.only = TRUE)       
}

So just call this function before any library calls, and things should be fine.

Edited to add: oops, I see in the comments you found similar solutions already. Well, my function looks cleaner than those, and safer....

user2554330
  • 37,248
  • 4
  • 43
  • 90
  • Oh cool, this looks safer. I ended up using [this](https://github.com/andrewheiss/rmarkdown-website-envs/blob/master/02_something-else.Rmd), which does a similar thing, but has `unload = TRUE`, which feels more dangerous, so I've removed it from my actual script now. – Andrew Apr 26 '18 at 21:14
  • Using `sessionInfo()$otherPkgs` bases the test on the package "priority", which is slightly different than basing it on whether they are defaults. For example, `tools` is typically not a default package, but it won't be detached by your code. – user2554330 Apr 26 '18 at 22:11