yihui / xfun

Miscellaneous R functions
https://yihui.org/xfun/
Other
141 stars 28 forks source link

xfun::pkg_load2() breaks renv caching #52

Closed chunyunma closed 3 years ago

chunyunma commented 3 years ago

I have been using xfun::pkg_load2() for some time. Recently, I noticed that in an renv initialized project, xfun::pkg_load2() would always download fresh copies of packages instead of creating symbolic links to their cached copies.

For example, running the next chunk in an renv initialized test project, to make sure that the listed packages have been globally cached.

cran_packages <- c(
                  "dplyr", 
                  "ggplot2",
                  "skimr", 
                  "magrittr"

)

install.packages(cran_packages)

Afterwards, delete the test project folder and start from scratch with the next chunk,

cran_packages <- c(
                  "dplyr", 
                  "ggplot2",
                  "skimr", 
                  "magrittr"

)

if (length(cran_packages) != 0) xfun::pkg_load2(cran_packages)

Running this chunk would trigger downloading packages from CRAN, even though they have already been cached by renv in a previous step.

In comparison, running the next chunk again in a freshly initialized project would not trigger downloading from CRAN, only linking to the cache.

cran_packages <- c(
                  "dplyr", 
                  "ggplot2",
                  "skimr", 
                  "magrittr"

)

install.packages(cran_packages)

Although this behaviour of xfun::pkg_load2() does not break anything, it does defeat the purpose of renv caching. I thought xfun::pkg_load2() and xfun::pkg_load() are both wrappers of some variation of the following:

if (!requireNamespace("pkg")) install.packages("pkg")

But this example tells me that there might be more differences than I previously thought. Is this true? Thank you very much!


By filing an issue to this repo, I promise that

I understand that my issue may be closed if I don't fulfill my promises.

yihui commented 3 years ago

I thought xfun::pkg_load2() and xfun::pkg_load() are both wrappers of some variation of the following

That's true.

https://github.com/yihui/xfun/blob/65c41737a59d39f191688a1d40d28d201d965709/R/packages.R#L62-L65

I don't know why install.packages() didn't work as expected in this case...

cderv commented 3 years ago

I think this is because xfun::pkg_load2() is not renv cache aware as you observe. Only renv::install() is aware of renv caching. However, there is a trick in renv so that when use interactively in an R session install.packages() is in fact using renv::install(). It is using shims as described in https://rstudio.github.io/renv/articles/renv.html#shims-1 But if you use directly utils::install_packages() I guess you will observe the same behavior.

xfun::pkg_install() is using utils::install_packages() I think, and not using the shim. I may mistaken but I think this is how it works.

As a workaround to test this theory, you can try setting this option

options(xfun.install.packages = renv::install)

then use pkg_load2() - the renv function should be used.

Does it make sense ?

If this works as I expect, we could do as we have done for pak, and maybe modify the behavior of pkg_install() to use renv::install when used in a renv project ? Or just by setting an alias option e.g options(xfun.install.packages = "renv") at the project level ?

chunyunma commented 3 years ago

@yihui @cderv Thank you very much for the quick reply! @cderv You were right!!! This makes total sense to me now. Once I added that option() line to my local .Rprofile, renv cache kicked in right away.

yihui commented 3 years ago

@cderv Great! Then the question is, how can I tell if I'm inside an renv project? If I can detect it, I can change the default value of the xfun.install.packages option to renv::install.https://github.com/yihui/xfun/blob/65c41737a59d39f191688a1d40d28d201d965709/R/revcheck.R#L353

cderv commented 3 years ago

That is exactly what I was thinking. I am unto that - as soon as I know the best way to determine that, I'll tell you :)

cderv commented 3 years ago

It seems that renv will set RENV_PROJECT env var when initialized and this is not something that a user should modified (at least it is not documented). So if this variable is set, then we are in a renv initialized project.

if this should do it

if (!is.na(Sys.getenv("RENV_PROJECT", unset = NA)) install = getOption('xfun.install.packages', renv::install) 

right ?

yihui commented 3 years ago

Okay. I'll do that. Thanks for the pointer!

yihui commented 3 years ago

@chunyunma With the current dev version, you no longer need to set the option:

remotes::install_github('yihui/xfun')
chunyunma commented 3 years ago

@yihui Sorry for the belated follow up. I finally had a chance to test xfun::pkg_load2() from the current dev version. I got the following error message when I ran xfun::pkg_load2():

Error in renv::install(pkgs, library = lib, ...) : 
  argument "lib" is missing, with no default

It seems that the latest update on the master branch has somehow erased the default of "lib" argument in renv::install(), even though .libPaths() is returning two two items, the first of which being the local one as expected, .../renv/library/R-4.0/x86_64-apple-darwin17.0. And I am not sure how to explicitly set lib inside xfun::pkg_load2() as I did not see that argument mentioned in the documentation (?xfun::pkg_load2()).

> xfun::session_info("xfun")
R version 4.0.4 (2021-02-15)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 10.16

Locale: en_CA.UTF-8 / en_CA.UTF-8 / en_CA.UTF-8 / C / en_CA.UTF-8 / en_CA.UTF-8

Package version:
  graphics_4.0.4  grDevices_4.0.4
  stats_4.0.4     tools_4.0.4    
  utils_4.0.4     xfun_0.22.3    

I am reverting back to the temporary solution of options(xfun.install.packages = renv::install) for now.

Thank you again!

yihui commented 3 years ago

My bad. Just fixed it. Thanks for the report again!