Skip to content

Commit

Permalink
store the error message and show it in Rscript_call(); this will be m…
Browse files Browse the repository at this point in the history
…uch more informational than the original error message
  • Loading branch information
yihui committed May 15, 2024
1 parent 87e52fe commit 6b8aa64
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 27 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: xfun
Type: Package
Title: Supporting Functions for Packages Maintained by 'Yihui Xie'
Version: 0.44.2
Version: 0.44.3
Authors@R: c(
person("Yihui", "Xie", role = c("aut", "cre", "cph"), email = "[email protected]", comment = c(ORCID = "0000-0003-0645-5666")),
person("Wush", "Wu", role = "ctb"),
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

- For `record()` with `verbose = 1` or `2`, invisible `NULL` is no longer printed.

- `Rscript_call()` will show the actual error message (if an error occurred) during calling the function in a new R session.

# CHANGES IN xfun VERSION 0.44

- Added a function `cache_exec()` to cache the execution of an expression either in memory or on disk. It is much more general and flexible than `cache_rds()`. For example, it supports custom reading/writing methods for cache files, and can load locally created variables in the expression while loading cache.
Expand Down
34 changes: 21 additions & 13 deletions R/command.R
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,23 @@ Rcmd = function(args, ...) {
#' Call a function in a new R session via `Rscript()`
#'
#' Save the argument values of a function in a temporary RDS file, open a new R
#' session via [Rscript()], read the argument values, call the
#' function, and read the returned value back to the current R session.
#' session via [Rscript()], read the argument values, call the function, and
#' read the returned value back to the current R session.
#' @param fun A function, or a character string that can be parsed and evaluated
#' to a function.
#' @param args A list of argument values.
#' @param options A character vector of options to passed to
#' [Rscript()], e.g., `"--vanilla"`.
#' @param options A character vector of options to passed to [Rscript()], e.g.,
#' `"--vanilla"`.
#' @param ...,wait Arguments to be passed to [system2()].
#' @param fail The desired error message when an error occurred in calling the
#' function.
#' function. If the actual error message during running the function is
#' available, it will be appended to this message.
#' @export
#' @return The returned value of the function in the new R session.
#' @return If `wait = TRUE`, the returned value of the function in the new R
#' session. If `wait = FALSE`, three file paths will be returned: the first
#' one stores `fun` and `args` (as a list), the second one is supposed to
#' store the returned value of the function, and the third one stores the
#' possible error message.
#' @examples factorial(10)
#' # should return the same value
#' xfun::Rscript_call('factorial', list(10))
Expand All @@ -86,16 +91,19 @@ Rcmd = function(args, ...) {
#' xfun::Rscript_call(factorial, list(10), options = c("--vanilla"))
Rscript_call = function(
fun, args = list(), options = NULL, ..., wait = TRUE,
fail = sprintf("Failed to run '%s' in a new R session.", deparse(substitute(fun))[1])
fail = sprintf("Failed to run '%s' in a new R session", deparse(substitute(fun))[1])
) {
f = replicate(2, tempfile(fileext = '.rds'))
on.exit(unlink(if (wait) f else f[2]), add = TRUE)
f = replicate(3, tempfile(fileext = '.rds'))
on.exit(if (wait) unlink(f), add = TRUE)
saveRDS(list(fun, args), f[1])
Rscript(
c(options, shQuote(c(pkg_file('scripts', 'call-fun.R'), f)))
,..., wait = wait
res = Rscript(
c(options, shQuote(c(pkg_file('scripts', 'call-fun.R'), f))), ..., wait = wait
)
if (wait) if (file_exists(f[2])) readRDS(f[2]) else stop(fail, call. = FALSE)
if (wait) {
if (res == 0) readRDS(f[2]) else stop(
fail, if (file_exists(f[3])) c(': ', readRDS(f[3])) else '.', call. = FALSE
)
} else f
}

# call a function in a background process
Expand Down
14 changes: 8 additions & 6 deletions inst/scripts/call-fun.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# This script is executed via the command line `Rscript call-fun.R arg1 arg2`,
# where arg1 is a path to an .rds file, which contains the function and its
# arguments saved as a list, and arg2 is a path to an .rds file to which the
# returned value of the function call is saved.
# This script is executed via the command line `Rscript call-fun.R arg1 arg2
# arg3`, where arg1 is a path to an .rds file, which contains the function and
# its arguments saved as a list; arg2 is a path to an .rds file to which the
# returned value of the function call is saved; arg3 saves the error message.

local({
if (length(a <- commandArgs(TRUE)) != 2)
stop('The number of arguments passed to Rscript should be 2.')
if (length(a <- commandArgs(TRUE)) != 3)
stop('The number of arguments passed to Rscript should be 3.')
# save the error message on exit if an error occurred
on.exit(if (!file.exists(a[2])) saveRDS(geterrmessage(), a[3]))
x = readRDS(a[1]) # list(fun, args)
f = x[[1]]
if (is.character(f)) f = eval(parse(text = f), envir = globalenv())
Expand Down
19 changes: 12 additions & 7 deletions man/Rscript_call.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6b8aa64

Please sign in to comment.