fs icon indicating copy to clipboard operation
fs copied to clipboard

path_real() mangles paths starting with ~ on Windows

Open mikmart opened this issue 6 years ago • 5 comments

On Windows, I get:

fs::path_real("~/R/dev")
#> C:/Users/mikko/AppData/Local/Temp/Rtmpa8cpqi/reprex37584fd22289/Users/mikko/R/dev

I would expect to get:

file.path(path.expand("~"), "R", "dev")
#> [1] "C:/Users/mikko/Documents/R/dev"

Created on 2019-09-28 by the reprex package (v0.3.0)

I ran into this as many usethis functions were failing due to path errors. Maybe related to #206?

Session info
devtools::session_info()
#> - Session info ----------------------------------------------------------
#>  setting  value                       
#>  version  R version 3.6.1 (2019-07-05)
#>  os       Windows 10 x64              
#>  system   x86_64, mingw32             
#>  ui       RTerm                       
#>  language (EN)                        
#>  collate  English_United States.1252  
#>  ctype    English_United States.1252  
#>  tz       Europe/Helsinki             
#>  date     2019-09-28                  
#> 
#> - Packages --------------------------------------------------------------
#>  package     * version    date       lib source                        
#>  assertthat    0.2.1      2019-03-21 [1] CRAN (R 3.6.0)                
#>  backports     1.1.4      2019-04-10 [1] CRAN (R 3.6.0)                
#>  callr         3.3.0      2019-07-04 [1] CRAN (R 3.6.1)                
#>  cli           1.1.0      2019-03-19 [1] CRAN (R 3.6.0)                
#>  crayon        1.3.4      2017-09-16 [1] CRAN (R 3.6.0)                
#>  desc          1.2.0      2018-05-01 [1] CRAN (R 3.6.0)                
#>  devtools      2.1.0      2019-07-06 [1] CRAN (R 3.6.1)                
#>  digest        0.6.20     2019-07-04 [1] CRAN (R 3.6.1)                
#>  evaluate      0.14       2019-05-28 [1] CRAN (R 3.6.0)                
#>  fs            1.3.1.9000 2019-09-28 [1] Github (r-lib/fs@380685c)     
#>  glue          1.3.1      2019-03-12 [1] CRAN (R 3.6.0)                
#>  highr         0.8        2019-03-20 [1] CRAN (R 3.6.0)                
#>  htmltools     0.3.6      2017-04-28 [1] CRAN (R 3.6.0)                
#>  knitr         1.23       2019-05-18 [1] CRAN (R 3.6.0)                
#>  magrittr      1.5        2014-11-22 [1] CRAN (R 3.6.0)                
#>  memoise       1.1.0      2017-04-21 [1] CRAN (R 3.6.0)                
#>  pkgbuild      1.0.3      2019-03-20 [1] CRAN (R 3.6.0)                
#>  pkgload       1.0.2      2018-10-29 [1] CRAN (R 3.6.0)                
#>  prettyunits   1.0.2      2015-07-13 [1] CRAN (R 3.6.0)                
#>  processx      3.4.0      2019-07-03 [1] CRAN (R 3.6.1)                
#>  ps            1.3.0      2018-12-21 [1] CRAN (R 3.6.0)                
#>  R6            2.4.0      2019-02-14 [1] CRAN (R 3.6.0)                
#>  Rcpp          1.0.2      2019-07-25 [1] CRAN (R 3.6.1)                
#>  remotes       2.1.0      2019-06-24 [1] CRAN (R 3.6.0)                
#>  rlang         0.4.0      2019-06-25 [1] CRAN (R 3.6.0)                
#>  rmarkdown     1.13       2019-05-22 [1] CRAN (R 3.6.0)                
#>  rprojroot     1.3-2      2018-01-03 [1] CRAN (R 3.6.0)                
#>  sessioninfo   1.1.1      2018-11-05 [1] CRAN (R 3.6.0)                
#>  stringi       1.4.3      2019-03-12 [1] CRAN (R 3.6.0)                
#>  stringr       1.4.0      2019-02-10 [1] CRAN (R 3.6.0)                
#>  testthat      2.1.1      2019-04-23 [1] CRAN (R 3.6.0)                
#>  usethis       1.5.1.9000 2019-09-28 [1] Github (r-lib/usethis@a2342b8)
#>  withr         2.1.2      2018-03-15 [1] CRAN (R 3.6.0)                
#>  xfun          0.8        2019-06-25 [1] CRAN (R 3.6.0)                
#>  yaml          2.2.0      2018-07-25 [1] CRAN (R 3.6.0)                
#> 
#> [1] C:/Users/mikko/Documents/R/win-library/3.6
#> [2] C:/Program Files/R/R-3.6.1/library

mikmart avatar Sep 28 '19 09:09 mikmart

One thing to know with fs is that it handles ~ differently than R base on windows. It uses USERPROFILE env var as home. See ?fs::path_expand

This is why you have fs::path_expand_r also to get the R default behavior if needed

fs::path_expand("~/R/dev")
#> C:/Users/chris/R/dev
fs::path_expand_r("~/R/dev")
#> C:/Users/chris/Documents/R/dev

fs::path_real uses fs::path_expand (see help page and source code). So on windows I think it explains what you don't get the expected result with Documents in the path.

What I can't explain is why fs::path_real uses the temp directory used by reprex when creating the canonical path... In fact, it uses the current working directory and add the expanded path. 🤔

library(fs)
directory <- dir_create(file_temp())
directory
#> C:/Users/chris/AppData/Local/Temp/Rtmp4Oe4ko/file5b58569770e8
setwd(directory)
path_expand("~/R/dev")
#> C:/Users/chris/R/dev
path_real("~/R/dev")
#> C:/Users/chris/AppData/Local/Temp/Rtmp4Oe4ko/file5b58569770e8/Users/chris/R/dev

Created on 2019-09-28 by the reprex package (v0.3.0)

I am not sure if it is intended or not.

Hope it helps.

cderv avatar Sep 28 '19 12:09 cderv

Thanks, I hadn't realized the difference in path_expand(). I think this means that my problem (which originated with usethis::use_test()) would not be fixed even if path_real() didn't expand in the working directory.

mikmart avatar Sep 28 '19 16:09 mikmart

Yeah I can reproduce this on Windows. If someone has written a path with a leading ~ on Windows, based on R's definition of home directory (so Documents/), the initial path_expand() in path_real() creates a nonexistent path. Then the logic for "partially existing" paths gets invoked, leading to the peculiar double expansion described above.

jennybc avatar Sep 30 '19 20:09 jennybc

This appears to be part of the problem. fs:::realize_("C:") returns the absolute path to current working directory.

fs:::realize_("C:")
#> [1] "C:\\Users\\jenny\\AppData\\Local\\Temp\\RtmporMOb5\\reprex1d345ab82c19"

Created on 2019-09-30 by the reprex package (v0.3.0.9000)

jennybc avatar Sep 30 '19 20:09 jennybc

Related to this issue, if the path does not exist, path_real() inserts the value of the current working directory:

packageVersion("fs")
#> [1] '1.3.1.9000'
fs::path_real("C:/Users/")
#> C:/Users
fs::path_real("C:/Users/non-existent")
#> C:/Users/john/AppData/Local/Temp/Rtmp6jGsyB/reprex4f867412f09/Users/non-existent

Created on 2019-10-08 by the reprex package (v0.3.0.9000)

This behavior was introduced in fs version 1.2.7:

packageVersion("fs")
#> [1] '1.2.7'
fs::path_real("C:/Users/")
#> C:/Users
fs::path_real("C:/Users/non-existent")
#> C:/Users/john/AppData/Local/Temp/Rtmp4aK0dQ/reprex371838e373c/Users/non-existent

Created on 2019-10-08 by the reprex package (v0.3.0.9000)

In fs 1.2.6, path_real() threw an error if the file path did not exist:

packageVersion("fs")
#> [1] '1.2.6'
fs::path_real("C:/Users/")
#> C:/Users
fs::path_real("C:/Users/non-existent")
#> Error: [ENOENT] Failed to realize 'C:/Users/non-existent': no such file or directory

Created on 2019-10-08 by the reprex package (v0.3.0.9000)

jdblischak avatar Oct 08 '19 13:10 jdblischak