From 09825ee4cd40e2fa7059b45a087fd0a0bca16bb8 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Mon, 4 Jun 2012 17:05:54 -0400 Subject: [PATCH] closes #228: do not hard-code globalenv() as the environment for knit() any more; the default envir is parent.frame() for knit() now --- R/block.R | 11 ++++++----- R/cache.R | 12 ++++++------ R/output.R | 5 ++++- R/utils.R | 6 +++--- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/R/block.R b/R/block.R index dff39a7368..8d24a01ad6 100644 --- a/R/block.R +++ b/R/block.R @@ -61,7 +61,7 @@ block_exec = function(params) { options = params ## eval chunks (in an empty envir if cache) - env = if (options$cache) new.env(parent = globalenv()) else globalenv() + env = if (options$cache) new.env(parent = knit_global()) else knit_global() .knitEnv$knit_env = env # make a copy of the envir obj.before = ls(globalenv(), all.names = TRUE) # global objects before chunk @@ -172,7 +172,7 @@ block_exec = function(params) { output = str_c(unlist(wrap(res, options)), collapse = '') # wrap all results together res.after = run_hooks(before = FALSE, options, env) # run 'after' hooks - if (options$cache) copy_env(env, globalenv()) + if (options$cache) copy_env(env, knit_global()) output = str_c(c(res.before, output, res.after), collapse = '') # insert hook results output = if (length(output) == 0L) '' else knit_hooks$get('chunk')(output, options) @@ -180,7 +180,8 @@ block_exec = function(params) { if (options$cache) { obj.after = ls(globalenv(), all.names = TRUE) # figure out new global objs - objs = c(ls(env, all.names = TRUE), setdiff(obj.after, obj.before)) + copy_env(globalenv(), knit_global(), setdiff(obj.after, obj.before)) + objs = ls(env, all.names = TRUE) block_cache(options, output, objs) if (options$autodep) cache$objects(objs, code, options$label, options$cache.path) } @@ -191,7 +192,7 @@ block_exec = function(params) { block_cache = function(options, output, objects) { hash = options$hash outname = str_c('.', hash) - assign(outname, output, envir = globalenv()) + assign(outname, output, envir = knit_global()) ## purge my old cache and cache of chunks dependent on me cache$purge(str_c(valid_path(options$cache.path, c(options$label, dep_list$get(options$label))), '_*')) @@ -218,7 +219,7 @@ inline_exec = function(block) { owd = setwd(input_dir()); on.exit(setwd(owd)) loc = block$location for (i in 1:n) { - res = try(eval(parse(text = code[i]), envir = globalenv())) + res = try(eval(parse(text = code[i]), envir = knit_global())) d = nchar(input) ## replace with evaluated results str_sub(input, loc[i, 1], loc[i, 2]) = if (length(res)) { diff --git a/R/cache.R b/R/cache.R index 4f73937518..cb185dc133 100644 --- a/R/cache.R +++ b/R/cache.R @@ -17,10 +17,10 @@ new_cache = function() { cache_save = function(keys, outname, hash) { # keys are new variables created; outname is the text output of a chunk path = cache_path(hash) - save(list = outname, file = str_c(path, '.RData'), envir = globalenv()) + save(list = outname, file = str_c(path, '.RData'), envir = knit_global()) # FIXME: after R 2.15.0, this line can be removed keys = setdiff(keys, '.Random.seed') - tools:::makeLazyLoadDB(globalenv(), path, variables = keys) + tools:::makeLazyLoadDB(knit_global(), path, variables = keys) } save_objects = function(objs, label, path) { @@ -43,10 +43,10 @@ new_cache = function() { cache_load = function(hash) { path = cache_path(hash) - lazyLoad(path, envir = globalenv()) + lazyLoad(path, envir = knit_global()) # load output from last run if exists if (file.exists(path2 <- str_c(path, '.RData'))) { - load(path2, envir = globalenv()) + load(path2, envir = knit_global()) } } @@ -69,8 +69,8 @@ new_cache = function() { ## code output is stored in .[hash], so cache=TRUE won't lose output as cacheSweave does cache_output = function(hash) { - if (exists(str_c('.', hash), envir = globalenv(), mode = 'character')) { - get(str_c('.', hash), envir = globalenv(), mode = 'character') + if (exists(str_c('.', hash), envir = knit_global(), mode = 'character')) { + get(str_c('.', hash), envir = knit_global(), mode = 'character') } else '' } diff --git a/R/output.R b/R/output.R index 926a8117fe..0c155d10bc 100644 --- a/R/output.R +++ b/R/output.R @@ -53,6 +53,8 @@ #' \code{\link[utils]{Stangle}}) #' @param text a character vector as an alternative way to provide the input #' file +#' @param envir the environment in which the code chunks are to be evaluated +#' (can use \code{\link{new.env}()} to guarantee an empty new environment) #' @return The compiled document is written into the output file, and the path #' of the output file is returned, but if the \code{output} path is #' \code{NULL}, the output is returned as a character vector. @@ -90,7 +92,7 @@ #' ## or setwd(dirname(f)); knit(basename(f)) #' #' purl(f) # extract R code only -knit = function(input, output = NULL, tangle = FALSE, text = NULL) { +knit = function(input, output = NULL, tangle = FALSE, text = NULL, envir = parent.frame()) { in.file = !missing(input) && is.character(input) # is a file input optk = opts_knit$get(); on.exit(opts_knit$set(optk), add = TRUE) @@ -167,6 +169,7 @@ knit = function(input, output = NULL, tangle = FALSE, text = NULL) { progress = opts_knit$get('progress') if (in.file) message(ifelse(progress, '\n\n', ''), 'processing file: ', input) + .knitEnv$knit_global = envir # the envir to eval code res = process_file(text, output) cat(res, file = if (is.null(output)) '' else output) dep_list$restore() # empty dependency list diff --git a/R/utils.R b/R/utils.R index ae627479b0..e7e5574e27 100644 --- a/R/utils.R +++ b/R/utils.R @@ -30,7 +30,7 @@ comment_out = function(x, options) { ## assign string in comments to a global variable comment_to_var = function(x, varname, pattern) { if (str_detect(x, pattern)) { - assign(varname, str_replace(x, pattern, ''), envir = globalenv()) + assign(varname, str_replace(x, pattern, ''), envir = knit_global()) return(TRUE) } FALSE @@ -287,11 +287,11 @@ fix_options = function(options) { ## try eval an option (character) to its value eval_opt = function(x) { if (!is.character(x)) return(x) - eval(parse(text = x), envir = globalenv()) + eval(parse(text = x), envir = knit_global()) } ## eval options as symbol/language objects -eval_lang = function(x, envir = globalenv()) { +eval_lang = function(x, envir = knit_global()) { if (!is.symbol(x) && !is.language(x)) return(x) eval(x, envir = envir) }