Search code examples
rr-environment

attaching packages to a "temporary" search path in R


Inside a function, I am sourcing a script:

f <- function(){
  source("~/Desktop/sourceme.R") # source someone elses script
  # do some stuff to the variables read in
}
f()
search() # library sourceme.R attaches is all the way in the back!

and unfortunately, the scripts that I am sourcing are not fully under my control. They make calls to library(somePackage), and it pollutes the search path.

This is mostly a problem if the author of sourceme.R expects the package that he/she is attaching to be at the top level/close to the global environment. If I myself have attached some package that masks some of the function names he/she is expecting to be available, then that's no good.

Is there a way I can source scripts but somehow make my own temporary search path that "resets" after the function is finished running?


Solution

  • I would consider sourcing the script in a separate R process using the callr package and then return the environment created by the sourced file.

    By using a separate R process, this will prevent your search path from being polluted. I'm guessing there maybe some side effects (such as defining new functions of variables) in your global environment you do want. The local argument of the source functions allows you to specify where the parsed script should be executed. If you return this environment from the other R process, you can access any result you need.

    Not sure what yours looks like but say I have this file that would modify the search path:

    # messWithSearchPath.R
    
    library(dplyr)
    
    a <- data.frame(groupID = rep(1:3, 10), value = rnorm(30))
    
    b <- a %>% 
      group_by(groupID) %>% 
      summarize(agg = sum(value))
    

    From my top level script, I would write a wrapper function to source it in a new environment and have callr execute this function:

    RogueScript <- function(){
      
      rogueEnv <- new.env()
      
      source("messWIthSearchPath.R", local = rogueEnv)
      
      rogueEnv
      
    }
    
    before <- search()
    
    scriptResults <- callr::r(RogueScript)
    
    scriptResults$b
    #>   groupID       agg
    #> 1       1 -2.871642
    #> 2       2  3.368499
    #> 3       3  1.159509
    
    identical(before, search())
    #> [1] TRUE
    

    If the scripts have other side effects (such as setting options or establishing external connections), this method probably won't work. There may be workarounds depending on what they are intended to do, but this should work if you just want the variables/functions created. It also prevents the scripts from conflicting with each other not just your top level script.