From 92230250e1fb880e8b86b57c2b4f96c6012c3547 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Fri, 21 Jun 2024 16:39:12 +0100 Subject: [PATCH] Convert stop_on_error to an enum (#168) This makes it easier for me to understand since I don't need to keep looking up the docs. --- R/eval.R | 37 +++++++++++++++++++++++++---------- man/evaluate.Rd | 15 +++++++++----- tests/testthat/_snaps/eval.md | 8 ++++++++ tests/testthat/test-eval.R | 8 ++++++++ 4 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 tests/testthat/_snaps/eval.md diff --git a/R/eval.R b/R/eval.R index 33e2055..17b8dae 100644 --- a/R/eval.R +++ b/R/eval.R @@ -15,11 +15,15 @@ #' the parent environment to `envir`. #' @param debug if `TRUE`, displays information useful for debugging, #' including all output that evaluate captures. -#' @param stop_on_error if `2`, evaluation will halt on first error and you -#' will get no results back. If `1`, evaluation will stop on first error -#' without signaling the error, and you will get back all results up to that -#' point. If `0` will continue running all code, just as if you'd pasted -#' the code into the command line. +#' @param stop_on_error A number between 0 and 2 that controls what happens +#' when the code errors: +#' +#' * If `0`, the default, will continue running all code, just as if you'd +#' pasted the code into the command line. +#' * If `1`, evaluation will stop on first error without signaling the error, +#' and you will get back all results up to that point. +#' * If `2`, evaluation will halt on first error and you will get back no +#' results. #' @param keep_warning,keep_message whether to record warnings and messages; if #' `FALSE`, messages will be suppressed; if `NA`, they will not be captured #' (normally they will be sent to the console). Note that if the environment @@ -50,8 +54,8 @@ evaluate <- function(input, output_handler = NULL, filename = NULL, include_timing = FALSE) { - stop_on_error <- as.integer(stop_on_error) - stopifnot(length(stop_on_error) == 1) + + on_error <- check_stop_on_error(stop_on_error) # if this env var is set to true, always bypass messages if (env_var_is_true('R_EVALUATE_BYPASS_MESSAGES')) { @@ -65,7 +69,7 @@ evaluate <- function(input, warning("`evaluate(include_timing)` is deprecated") } - parsed <- parse_all(input, filename, stop_on_error != 2L) + parsed <- parse_all(input, filename, on_error != "error") if (inherits(err <- attr(parsed, 'PARSE_ERROR'), 'error')) { source <- new_source(parsed$src, expression(), output_handler$source) output_handler$error(err) @@ -90,7 +94,7 @@ evaluate <- function(input, src = parsed$src[[i]], watcher = watcher, envir = envir, - use_try = stop_on_error != 2L, + use_try = on_error != "error", keep_warning = keep_warning, keep_message = keep_message, log_warning = log_warning, @@ -98,7 +102,7 @@ evaluate <- function(input, ) watcher$check_devices() - if (stop_on_error > 0L && watcher$has_errored()) { + if (on_error == "stop" && watcher$has_errored()) { break } } @@ -236,3 +240,16 @@ reset_call <- function(cnd) { } cnd } + +check_stop_on_error <- function(x) { + if (is.numeric(x) && length(x) == 1 && !is.na(x)) { + if (x == 0L) { + return("continue") + } else if (x == 1L) { + return("stop") + } else if (x == 2L) { + return("error") + } + } + stop("`stop_on_error` must be 0, 1, or 2 ", call. = FALSE) +} diff --git a/man/evaluate.Rd b/man/evaluate.Rd index d76f7ba..bd0eb7a 100644 --- a/man/evaluate.Rd +++ b/man/evaluate.Rd @@ -32,11 +32,16 @@ the parent environment to \code{envir}.} \item{debug}{if \code{TRUE}, displays information useful for debugging, including all output that evaluate captures.} -\item{stop_on_error}{if \code{2}, evaluation will halt on first error and you -will get no results back. If \code{1}, evaluation will stop on first error -without signaling the error, and you will get back all results up to that -point. If \code{0} will continue running all code, just as if you'd pasted -the code into the command line.} +\item{stop_on_error}{A number between 0 and 2 that controls what happens +when the code errors: +\itemize{ +\item If \code{0}, the default, will continue running all code, just as if you'd +pasted the code into the command line. +\item If \code{1}, evaluation will stop on first error without signaling the error, +and you will get back all results up to that point. +\item If \code{2}, evaluation will halt on first error and you will get back no +results back. +}} \item{keep_warning, keep_message}{whether to record warnings and messages; if \code{FALSE}, messages will be suppressed; if \code{NA}, they will not be captured diff --git a/tests/testthat/_snaps/eval.md b/tests/testthat/_snaps/eval.md new file mode 100644 index 0000000..ac5ce00 --- /dev/null +++ b/tests/testthat/_snaps/eval.md @@ -0,0 +1,8 @@ +# check_stop_on_error converts integer to enum + + Code + check_stop_on_error(4) + Condition + Error: + ! `stop_on_error` must be 0, 1, or 2 + diff --git a/tests/testthat/test-eval.R b/tests/testthat/test-eval.R index cf1d048..f6553e0 100644 --- a/tests/testthat/test-eval.R +++ b/tests/testthat/test-eval.R @@ -84,3 +84,11 @@ test_that("multiple lines of comments do not lose the terminating \\n", { expect_output_types(ev, c("source", "source")) expect_equal(ev[[1]]$src, "# foo\n") }) + +test_that("check_stop_on_error converts integer to enum", { + expect_equal(check_stop_on_error(0), "continue") + expect_equal(check_stop_on_error(1), "stop") + expect_equal(check_stop_on_error(2), "error") + + expect_snapshot(check_stop_on_error(4), error = TRUE) +})