Skip to contents

1: Safely set autos

If you would like to account for potential syntax errors when sourcing in your autos, you can wrap rprofile() in purrr::safely(). This function will attempt to set the autos and return a list containing the result and any error that occurred.

Let’s show an example. First we’ll make an example configuration file that will automatically source your autos. We’ll intentionally add a syntax error to show how safely() works.

Here is our configuration file, which will automatically source the autos from the DEV and PROD directories:

default:
  autos:
    projects: !expr list(
      "DEV" = file.path("demo", "DEV", "username", "project1", "functions"),
      "PROD" = file.path("demo", "PROD", "project1", "functions")
      )

We will do a little work here to create the directory, place a script into the directory. We’ll add a syntax error by leaving off a closing } in test_error.R script in the PROD folder.

# create the temp directory
dir <- fs::file_temp()
dir.create(dir)
dir.create(file.path(dir, "/demo/PROD/project1/functions"), recursive = TRUE)

# write a function to the folder with an error
file_conn <- file(file.path(dir, "/demo/PROD/project1/functions/test_error.R"))
writeLines(
"test <- function(){print('test')", file_conn)
close(file_conn)

# write the config
config_path <- file.path(dir, "_envsetup.yml")
file_conn <- file(config_path)
writeLines(
  paste0(
"default:
  autos:
    PROD: '", dir,"/demo/PROD/project1/functions'"
  ), file_conn)
close(file_conn)

So if we call rprofile() passing in this config file, we will get an error because of the syntax error in test_error.R:

library(envsetup)
#> 
#> Attaching package: 'envsetup'
#> The following object is masked from 'package:base':
#> 
#>     library

envsetup_config <- config::get(file = config_path)

rprofile(envsetup_config)
#> Attaching paths to envsetup:paths
#> Error in `map2()`:
#>  In index: 1.
#>  With name: PROD.
#> Caused by error in `map()`:
#>  In index: 1.
#> Caused by error in `parse()`:
#> ! 2:0: unexpected end of input
#> 1: test <- function(){print('test')
#>    ^

To handle this error, we can use purrr::safely() to wrap the rprofile() function. This will allow us to catch the error and handle it gracefully.

safely_rprofile <- purrr::safely(rprofile)

ret <- safely_rprofile(envsetup_config)
#> Attaching paths to envsetup:paths

We still have an error, but safely allow the setup to continue. We can check the result of the safely_rprofile() function to see if there was an error, identify the issue and correct the syntax error in the function.

# check for errors and return if any occurred
if(!is.null(ret$error)) {
  print(ret$error)
}
#> <error/purrr_error_indexed>
#> Error in `map2()`:
#>  In index: 1.
#>  With name: PROD.
#> Caused by error in `map()`:
#>  In index: 1.
#> Caused by error in `parse()`:
#> ! 2:0: unexpected end of input
#> 1: test <- function(){print('test')
#>    ^
#> ---
#> Backtrace:
#>      
#>   1. └─purrr (local) safely_rprofile(envsetup_config)
#>   2. ─purrr:::capture_error(.f(...), otherwise, quiet)
#>   3. └─base::tryCatch(...)
#>   4. └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   5. └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   6. └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   7.   └─envsetup (local) .f(...)
#>   8.     └─envsetup:::set_autos(config$autos)
#>   9.       └─purrr::walk2(...)
#>  10.         └─purrr::map2(.x, .y, .f, ..., .progress = .progress)
#>  11.           └─purrr:::map2_("list", .x, .y, .f, ..., .progress = .progress)
#>  12. ─purrr:::with_indexed_errors(...)
#>  13. └─base::withCallingHandlers(...)
#>  14. purrr:::call_with_cleanup(...)
#>  15.             └─envsetup (local) .f(.x[[i]], .y[[i]], ...)
#>  16.               └─envsetup:::attach_auto(.x, .y)
#>  17.                 └─purrr::walk(...)
#>  18.                   └─purrr::map(.x, .f, ..., .progress = .progress)
#>  19.                     └─purrr:::map_("list", .x, .f, ..., .progress = .progress)
#>  20. ─purrr:::with_indexed_errors(...)
#>  21. └─base::withCallingHandlers(...)
#>  22. purrr:::call_with_cleanup(...)
#>  23.                       └─base (local) .f(.x[[i]], ...)
#>  24.                         └─base::parse(n = -1, file = file, srcfile = NULL, keep.source = FALSE)