diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd index fee1a6336f6..d4c8ebeef0e 100644 --- a/doc/NEWS.Rd +++ b/doc/NEWS.Rd @@ -177,6 +177,10 @@ \item The matrix multiplication functions \code{crossprod()} and \code{tcrossprod()} are now also primitive and S3 generic, as \code{\%*\%} had become in \R 4.3.0. + + \item \code{source()} and \code{example()} get a new optional argument + \code{catch.aborts} which allows continued evaluation of the \R code + after an error. } } diff --git a/src/library/base/R/source.R b/src/library/base/R/source.R index f07b323bb64..24964099219 100644 --- a/src/library/base/R/source.R +++ b/src/library/base/R/source.R @@ -24,6 +24,7 @@ function(file, local = FALSE, echo = verbose, print.eval = echo, max.deparse.length = 150, width.cutoff = 60L, deparseCtrl = "showAttributes", ## rather? c("keepInteger", "showAttributes", "keepNA"), chdir = FALSE, + catch.aborts = FALSE, encoding = getOption("encoding"), continue.echo = getOption("continue"), skip.echo = 0, keep.source = getOption("keep.source")) @@ -222,7 +223,11 @@ function(file, local = FALSE, echo = verbose, print.eval = echo, } } if (!tail) { - yy <- withVisible(eval(ei, envir)) + yy <- if(catch.aborts) + withRestarts( + withVisible(eval(ei, envir)), + abort = function() list(value = NULL, visible = FALSE)) + else withVisible(eval(ei, envir)) i.symbol <- mode(ei[[1L]]) == "name" if (!i.symbol) { ## ei[[1L]] : the function "<-" or other diff --git a/src/library/base/man/source.Rd b/src/library/base/man/source.Rd index f2e4a7812fa..91cc7a5e854 100644 --- a/src/library/base/man/source.Rd +++ b/src/library/base/man/source.Rd @@ -27,6 +27,7 @@ source(file, local = FALSE, echo = verbose, print.eval = echo, max.deparse.length = 150, width.cutoff = 60L, deparseCtrl = "showAttributes", chdir = FALSE, + catch.aborts = FALSE, encoding = getOption("encoding"), continue.echo = getOption("continue"), skip.echo = 0, keep.source = getOption("keep.source")) @@ -84,6 +85,8 @@ withAutoprint(exprs, evaluated = FALSE, local = parent.frame(), \item{chdir}{logical; if \code{TRUE} and \code{file} is a pathname, the \R working directory is temporarily changed to the directory containing \code{file} for evaluating.} + \item{catch.aborts}{logical indicating that \dQuote{abort}ing errors + should be caught.} \item{encoding}{character vector. The encoding(s) to be assumed when \code{file} is a character string: see \code{\link{file}}. A possible value is \code{"unknown"} when the encoding is guessed: see @@ -191,7 +194,7 @@ if(someCond) withAutoprint({ ## If you want to source() a bunch of files, something like ## the following may be useful: sourceDir <- function(path, trace = TRUE, ...) { - op <- options(); on.exit(options(op)) # to reset after each + op <- options(); on.exit(options(op)) # to reset after each for (nm in list.files(path, pattern = "[.][RrSsQq]$")) { if(trace) cat(nm,":") source(file.path(path, nm), ...) @@ -208,6 +211,16 @@ stopifnot(identical(x, 1:2), identical(y, x^2)) withAutoprint({ formals(sourceDir); body(sourceDir) }, max.deparse.length = 20, verbose = TRUE) %% --> tests in ../../../../tests/eval-etc.R + +## Continuing after (catchable) errors: +tc <- textConnection('1:3 + 2 + "3" + cat(" .. in spite of error: happily continuing! ..\n") + 6*7') +r <- source(tc, catch.aborts = TRUE) +## Error in 2 + "3" .... +## .. in spite of error: happily continuing! .. +stopifnot(identical(r, list(value = 42, visible=TRUE))) } \keyword{file} \keyword{programming} diff --git a/src/library/utils/R/example.R b/src/library/utils/R/example.R index d2388f3a77a..e1e705778cb 100644 --- a/src/library/utils/R/example.R +++ b/src/library/utils/R/example.R @@ -23,6 +23,7 @@ function(topic, package = NULL, lib.loc = NULL, type = c("console", "html"), echo = TRUE, verbose = getOption("verbose"), setRNG = FALSE, ask = getOption("example.ask"), prompt.prefix = abbreviate(topic, 6), + catch.aborts = FALSE, run.dontrun = FALSE, run.donttest = interactive()) { type <- match.arg(type) @@ -128,5 +129,6 @@ function(topic, package = NULL, lib.loc = NULL, prompt.echo = paste0(prompt.prefix, getOption("prompt")), continue.echo = paste0(prompt.prefix, getOption("continue")), verbose = verbose, max.deparse.length = Inf, encoding = "UTF-8", + catch.aborts = catch.aborts, skip.echo = skips, keep.source=TRUE) } diff --git a/src/library/utils/man/example.Rd b/src/library/utils/man/example.Rd index e5445c41faa..657f2ce28bd 100644 --- a/src/library/utils/man/example.Rd +++ b/src/library/utils/man/example.Rd @@ -18,6 +18,7 @@ example(topic, package = NULL, lib.loc = NULL, verbose = getOption("verbose"), setRNG = FALSE, ask = getOption("example.ask"), prompt.prefix = abbreviate(topic, 6), + catch.aborts = FALSE, run.dontrun = FALSE, run.donttest = interactive()) } \arguments{ @@ -60,6 +61,8 @@ example(topic, package = NULL, lib.loc = NULL, device and to any devices opened by the example code.} \item{prompt.prefix}{character; prefixes the prompt to be used if \code{echo} is true (as it is by default).} + \item{catch.aborts}{logical, passed on to \code{\link{source}()}, + indicating that \dQuote{abort}ing errors should be caught.} \item{run.dontrun}{logical indicating that \verb{\dontrun} should be ignored.} \item{run.donttest}{logical indicating that \verb{\donttest}