Source script to separate environment in R, not the global environment
The simplest way to source a script as if it was a package (i.e. such that lexical scoping won't result in the use of variables defined in the global environment when calling functions defined in your R script) is to create an environment that that is whose parent is the .BaseNamespaceEnv
, and then call source()
using that environment.
For example if you have a script like this:
# << my-script.R >>
my_fun <- function(x){x + y}
Then evaluating the following at the console, won't generate an error, as it would if my_fun
were defined within it's own package:
source("my-script.R")
y = 2
my_fun(1)
#> 3
However, if you create an environment whose search()
path does not include the Global Environment (.GlobalEnv
) then you'll get a proper error when you call the function from your script:
# Create the environment:
ENV = new.env(parent = .BaseNamespaceEnv)
# Attache it to the search path so that objects in your environment can be
# found from the global environment (i.e. from the console):
attach(ENV)
# do things:
source("my-script.R",ENV)
y = 2
my_fun(1)
#> Error in .ENV$my_fun(3) : object 'y' not found
The following environment insertion appears to achieve the desired functionality:
Check the current search path:
search()
# [1] ".GlobalEnv" "package:stats" "package:graphics"
# [4] "package:grDevices" "package:utils" "package:datasets"
# [7] "package:methods" "Autoloads" "package:base"
Add new environment for sourced packages and use local
parameter when source()
ing:
myEnv <- new.env()
source("some_other_script.R", local=myEnv)
attach(myEnv, name="sourced_scripts")
Check the search path:
search()
# [1] ".GlobalEnv" "sourced_scripts" "package:dplyr"
# [4] "package:stats" "package:graphics" "package:grDevices"
# [7] "package:utils" "package:datasets" "package:methods"
# [10] "Autoloads" "package:base"
Note that we attach()
the new environment after sourcing, so that dplyr
is attached after our script environment in the search path.
From the source
documentation, the local
argument can be an environment which determines where the sourced expressions are evaluated.
This suggests that you could create a new environment, run source
passing this environment to local
, then attach
the environment to the search path.
Or you can use attach with what=NULL
to create an empty environment, save the return value, and pass that to local
in source
:
tmp <- attach(what=NULL)
source('test.R', local=tmp)
or as a single line:
source('test.R', local=attach(NULL))
I would also like to a solution using sys.source
function. Using envir
and toplevel.env
arguments allows for convenient (IMHO) bypassing of the global environment. As per the linked documentation:
sys.source
[p]arses expressions in the given file, and then successively evaluates them in the specified environment.
tstEnv <- new.env()
sys.source(file = "tst.R", envir = tstEnv, toplevel.env = tstEnv)
where tst.R
contains:
a <- 1
b <- 1
Results:
ls(envir = .GlobalEnv)
# [1] "tstEnv"
ls(envir = tstEnv)
# [1] "a" "b"
tstEnv$a
# [1] 1