diff --git a/DESCRIPTION b/DESCRIPTION index af3d90d..9454c8e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -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 = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), person("Wush", "Wu", role = "ctb"), diff --git a/NEWS.md b/NEWS.md index 05868ce..7cf02dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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. diff --git a/R/command.R b/R/command.R index 77f6548..4c70f90 100644 --- a/R/command.R +++ b/R/command.R @@ -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)) @@ -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 diff --git a/inst/scripts/call-fun.R b/inst/scripts/call-fun.R index a0523c3..5017cd9 100644 --- a/inst/scripts/call-fun.R +++ b/inst/scripts/call-fun.R @@ -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()) diff --git a/man/Rscript_call.Rd b/man/Rscript_call.Rd index eb145eb..351b3a8 100644 --- a/man/Rscript_call.Rd +++ b/man/Rscript_call.Rd @@ -10,7 +10,7 @@ Rscript_call( 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]) ) } \arguments{ @@ -19,21 +19,26 @@ to a function.} \item{args}{A list of argument values.} -\item{options}{A character vector of options to passed to -\code{\link[=Rscript]{Rscript()}}, e.g., \code{"--vanilla"}.} +\item{options}{A character vector of options to passed to \code{\link[=Rscript]{Rscript()}}, e.g., +\code{"--vanilla"}.} \item{..., wait}{Arguments to be passed to \code{\link[=system2]{system2()}}.} \item{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.} } \value{ -The returned value of the function in the new R session. +If \code{wait = TRUE}, the returned value of the function in the new R +session. If \code{wait = FALSE}, three file paths will be returned: the first +one stores \code{fun} and \code{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. } \description{ Save the argument values of a function in a temporary RDS file, open a new R -session via \code{\link[=Rscript]{Rscript()}}, read the argument values, call the -function, and read the returned value back to the current R session. +session via \code{\link[=Rscript]{Rscript()}}, read the argument values, call the function, and +read the returned value back to the current R session. } \examples{ factorial(10)