r-lib / conflicted

An alternative conflict resolution strategy for R
https://conflicted.r-lib.org/
Other
247 stars 14 forks source link

Using `conflicts_prefer()` et al. in `.Rprofile` no longer works #91

Open uhkeller opened 1 year ago

uhkeller commented 1 year ago

Using conflicts_prefer() in an .Rprofile file no longer works, though from the console output it appears to.

I'm sure that it used to work before. Since the last release of {conflicted} was in February and I'm pretty sure it has worked after that for some time, maybe a change in R 4.3.0, or even 4.3.1, is causing this?

Steps to reproduce:

In an empty folder, create an .Rprofile file with the following contents:

# .Rprofile
library(conflicted)
library(dplyr)
conflicts_prefer(dplyr::filter)

Start R in this folder. Output:

R version 4.3.1 (2023-06-16) -- "Beagle Scouts"
[…]
[conflicted] Will prefer dplyr::filter over any other package.

But dplyr::filter is not in fact preferred:

> filter
function (x, filter, method = c("convolution", "recursive"),
[…]
<environment: namespace:stats>

Running the call to conflicts_prefer() again, on the command line or by sourcing .Rprofile, works:

> conflicts_prefer(dplyr::filter)
[conflicted] Removing existing preference.
[conflicted] Will prefer dplyr::filter over any other package.
> filter
function (.data, ..., .by = NULL, .preserve = FALSE)
[…]
<environment: namespace:dplyr>

The same happens when using conflict_prefer() instead.

Edit: sessionInfo() after starting R

> sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.5.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/Luxembourg
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
[1] dplyr_1.1.2      conflicted_1.2.0

loaded via a namespace (and not attached):
 [1] utf8_1.2.3       R6_2.5.1         fastmap_1.1.1    tidyselect_1.2.0
 [5] magrittr_2.0.3   cachem_1.0.8     glue_1.6.2       tibble_3.2.1
 [9] pkgconfig_2.0.3  memoise_2.0.1    generics_0.1.3   lifecycle_1.0.3
[13] cli_3.6.1        fansi_1.0.4      vctrs_0.6.3      compiler_4.3.1
[17] pillar_1.9.0     rlang_1.1.1
jennybc commented 1 year ago

I am able to reproduce what OP reports in a terminal:

[conflicted] Will prefer dplyr::filter over any other package.
> search()
 [1] ".GlobalEnv"         "package:stats"      "package:graphics"  
 [4] "package:grDevices"  "package:utils"      "package:datasets"  
 [7] ".conflicts"         "package:dplyr"      "package:conflicted"
[10] "package:methods"    "Autoloads"          "package:base"      
> filter(mtcars, mpg > 27)
Error: object 'mpg' not found
> dplyr::filter(mtcars, mpg > 27)
                mpg cyl disp  hp drat    wt  qsec vs am gear carb
Fiat 128       32.4   4 78.7  66 4.08 2.200 19.47  1  1    4    1
Honda Civic    30.4   4 75.7  52 4.93 1.615 18.52  1  1    4    2
Toyota Corolla 33.9   4 71.1  65 4.22 1.835 19.90  1  1    4    1
Fiat X1-9      27.3   4 79.0  66 4.08 1.935 18.90  1  1    4    1
Lotus Europa   30.4   4 95.1 113 3.77 1.513 16.90  1  1    5    2

but not in RStudio:

[conflicted] Will prefer dplyr::filter over any other package.
> search()
 [1] ".GlobalEnv"         ".conflicts"         "tools:rstudio"      "package:stats"     
 [5] "package:graphics"   "package:grDevices"  "package:utils"      "package:datasets"  
 [9] "package:dplyr"      "package:conflicted" "package:methods"    "Autoloads"         
[13] "package:base"      
> filter(mtcars, mpg > 27)
                mpg cyl disp  hp drat    wt  qsec vs am gear carb
Fiat 128       32.4   4 78.7  66 4.08 2.200 19.47  1  1    4    1
Honda Civic    30.4   4 75.7  52 4.93 1.615 18.52  1  1    4    2
Toyota Corolla 33.9   4 71.1  65 4.22 1.835 19.90  1  1    4    1
Fiat X1-9      27.3   4 79.0  66 4.08 1.935 18.90  1  1    4    1
Lotus Europa   30.4   4 95.1 113 3.77 1.513 16.90  1  1    5    2
hadley commented 1 year ago

Here's a minimal reprex:

writeLines(con = ".Rprofile", c(
  "library(dplyr)",
  "conflicted::conflicts_prefer(dplyr::filter)"
))
environment(filter)
#> <environment: namespace:stats>
search()
#>  [1] ".GlobalEnv"        "package:stats"     "package:graphics" 
#>  [4] "package:grDevices" "package:utils"     "package:datasets" 
#>  [7] "package:methods"   "Autoloads"         "tools:callr"      
#> [10] "package:base"

Created on 2023-10-01 with reprex v2.0.2

I thought this might be because this code is run before the stats package is attached, and thus conflicted doesn't find any conflicts. But explicitly loading stats doesn't change anything:

writeLines(con = ".Rprofile", c(
  "library(stats)",
  "library(dplyr)",
  "conflicted::conflicts_prefer(dplyr::filter)"
))
environment(filter)
#> <environment: namespace:stats>
search()
#>  [1] ".GlobalEnv"        "package:stats"     "package:graphics" 
#>  [4] "package:grDevices" "package:utils"     "package:datasets" 
#>  [7] "package:methods"   "Autoloads"         "tools:callr"      
#> [10] "package:base"

Created on 2023-10-01 with reprex v2.0.2

hadley commented 1 year ago

Oh that's because my reprex isn't actually working; dplyr never gets loaded.

hadley commented 1 year ago

Ok, better reprex which illustrates that my initial guess at the problem was correct:

dir <- tempfile()
dir.create(dir)

writeLines(con = file.path(dir, ".Rprofile"), c(
  "library(dplyr)",
  "conflicted::conflicts_prefer(dplyr::filter)"
))
setwd(dir)
callr::r(user_profile = TRUE, function() {
  list(env =  environment(filter), search = search())
})
#> $env
#> <environment: namespace:stats>
#> 
#> $search
#>  [1] ".GlobalEnv"        "package:stats"     "package:graphics" 
#>  [4] "package:grDevices" "package:utils"     "package:datasets" 
#>  [7] ".conflicts"        "package:dplyr"     "package:methods"  
#> [10] "Autoloads"         "tools:callr"       "package:base"

Created on 2023-10-01 with reprex v2.0.2

dir <- tempfile()
dir.create(dir)

writeLines(con = file.path(dir, ".Rprofile"), c(
  "library(stats)",
  "library(dplyr)",
  "conflicted::conflicts_prefer(dplyr::filter)"
))
setwd(dir)
callr::r(user_profile = TRUE, function() {
  list(env =  environment(filter), search = search())
})
#> $env
#> <environment: namespace:dplyr>
#> 
#> $search
#>  [1] ".GlobalEnv"        "package:graphics"  "package:grDevices"
#>  [4] "package:utils"     "package:datasets"  ".conflicts"       
#>  [7] "package:dplyr"     "package:stats"     "package:methods"  
#> [10] "Autoloads"         "tools:callr"       "package:base"

Created on 2023-10-01 with reprex v2.0.2

hadley commented 1 year ago

If dplyr::filter() wasn't likely the most common package affected by this, I'd be tempted to leave it. But since it is, I think conflicted probably needs to detect if it's being called from .Rprofile, and if so, add a hook to run after loading is complete. (.First.sys() attaches each package from getOption("defaultPackages") in order, so could use the attach hook for the last package in that vector)