-
Notifications
You must be signed in to change notification settings - Fork 239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
safe expression evaluation with callr #174
Merged
Merged
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
902be58
whitespace
schloerke 9fd1520
add render_safe and run_safe
schloerke 6d2d5eb
comment for future cleanup
schloerke 691409f
Use a `safe` function rather than parameters to like `render_tutorial`
schloerke d55e833
added news item
schloerke da4e79a
add test for `safe({expr})`
schloerke 91bd682
code review update. Add env variable to set launch browser shiny arg
schloerke 04187b1
do not load rlang inside callr in safe(). it loads global namespace f…
schloerke File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,98 @@ | ||
|
||
#' Run a tutorial | ||
#' | ||
#' | ||
#' Run a tutorial which is contained within an R package. | ||
#' | ||
#' @param name Tutorial name (subdirectory within \code{tutorials/} | ||
#' | ||
#' @param name Tutorial name (subdirectory within \code{tutorials/} | ||
#' directory of installed package). | ||
#' @param package Name of package | ||
#' @param shiny_args Additional arguments to forward to | ||
#' \code{\link[shiny:runApp]{shiny::runApp}}. | ||
#' | ||
#' @param shiny_args Additional arguments to forward to | ||
#' \code{\link[shiny:runApp]{shiny::runApp}}. | ||
#' @param safe_session Boolean that determines if the exercises are evaluated | ||
#' in a new, safe R session. Should only be necessary when locally deployed. | ||
#' | ||
#' @details Note that when running a tutorial Rmd file with \code{run_tutorial} | ||
#' the tutorial Rmd should have already been rendered as part of the | ||
#' development of the package (i.e. the correponding tutorial .html file for | ||
#' the tutorial Rmd should have already been rendered as part of the | ||
#' development of the package (i.e. the correponding tutorial .html file for | ||
#' the .Rmd file must exist). | ||
#' | ||
#' | ||
#' @export | ||
run_tutorial <- function(name, package, shiny_args = NULL) { | ||
run_tutorial <- function(name, package, shiny_args = NULL, safe_session = FALSE) { | ||
|
||
# get path to tutorial | ||
tutorial_path <- system.file("tutorials", name, package = package) | ||
|
||
# validate that it's a direcotry | ||
if (!utils::file_test("-d", tutorial_path)) | ||
if (!utils::file_test("-d", tutorial_path)) | ||
stop("Tutorial ", name, " was not found in the ", package, " package.") | ||
|
||
# provide launch_browser if it's not specified in the shiny_args | ||
if (is.null(shiny_args)) | ||
shiny_args <- list() | ||
if (is.null(shiny_args$launch.browser)) | ||
shiny_args$launch.browser <- interactive() | ||
|
||
# run within tutorial wd and ensure we don't call rmarkdown::render | ||
withr::with_dir(tutorial_path, { | ||
withr::with_envvar(c(RMARKDOWN_RUN_PRERENDER = "0"), { | ||
rmarkdown::run(file = NULL, dir = tutorial_path, shiny_args = shiny_args) | ||
render_fn <- if (isTRUE(safe_session)) run_safe else rmarkdown::run | ||
render_fn(file = NULL, dir = tutorial_path, shiny_args = shiny_args) | ||
}) | ||
}) | ||
} | ||
|
||
|
||
safe_env <- function() { | ||
envs <- callr::rcmd_safe_env() | ||
envs[!(names(envs) %in% c("R_BROWSER"))] | ||
} | ||
|
||
|
||
callr_try_catch <- function(...) { | ||
tryCatch( | ||
..., | ||
# TODO when processx 3.2.0 is released, _downgrade_ to "interrupt" call instead of "system_command_interrupt". | ||
# https://github.com/r-lib/processx/issues/148 | ||
|
||
# if a user sends an interrupt, return silently | ||
system_command_interrupt = function() invisible(NULL) | ||
) | ||
} | ||
|
||
#' Render or Run documents in a new, safe R environment | ||
#' | ||
#' When rendering (or running) a document with R markdown, it inherits the current R Global environment. This will produce unexpected behaviors, such as poisoning the R Global environment with existing variables. By rendering the document in a new, safe R environment, a \emph{vanilla}, rendered document is produced. | ||
#' | ||
#' @param input,file Input file (R script, Rmd, or plain markdown). | ||
#' @param ... extra arguements to be passed to \code{rmarkdown::\link[rmarkdown]{render}} or | ||
#' \code{rmarkdown::\link[rmarkdown]{run}} | ||
#' @param show Logical, whether to show the standard output on the screen while the child process | ||
#' is running. Defaults to \code{TRUE}. | ||
#' @export | ||
#' @rdname render_safe | ||
render_safe <- function(input, ..., show = TRUE) { | ||
callr_try_catch({ | ||
callr::r( | ||
function(...) { | ||
rmarkdown::render(...) | ||
}, | ||
list(input = input, ...), | ||
show = show, | ||
env = safe_env() | ||
) | ||
}) | ||
} | ||
#' @export | ||
#' @rdname render_safe | ||
run_safe <- function(file, ..., show = TRUE) { | ||
callr_try_catch({ | ||
callr::r( | ||
function(...) { | ||
rmarkdown::run(...) | ||
}, | ||
list(file = file, ...), | ||
show = show, | ||
env = safe_env() | ||
) | ||
}) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to have a comment of why this function exists (because it's the same as the default for
callr::r
except that it allows opening a browser).