r语言 - 在 tryCatch 中处理警告之前的错误



>我正在处理一个同时抛出错误和警告的函数。(相关:关于警告的警告)

通常,警告会继续错误。在这些情况下,我想忽略警告,只处理错误。

另一方面,如果只有一个警告(没有错误),那么我想捕捉警告。

我正在尝试使用臭名昭著的易于使用的tryCatch

我的直接问题是:有没有办法强制tryCatchwarning之前处理error(或在出现错误时忽略警告)?

我从?tryCatch文档中的理解是,条件是处理FIFO的,在这种情况下,我的直接问题的答案是否定的 - 至少不是直接的。 在这种情况下,是否可以处理警告,然后让函数继续,同时仍然捕获错误?

我无法使用的解决方案:

  • suppressWarnings # 我仍然想捕获并处理警告
  • options(warn=2) # 某些警告是无害的
     relevant from `?tryCatch`

如果在评估 expr 时发出条件信号,则会检查已建立的处理程序,从最近建立的处理程序开始,以查找与条件类匹配的处理程序。当在单个 tryCatch 中提供多个处理程序时,第一个处理程序被认为比第二个处理程序更新。如果找到处理程序,则控制权将转移到建立处理程序的 tryCatch 调用,找到处理程序并取消建立所有更新的处理程序,使用条件作为其参数调用处理程序,并且处理程序返回的结果作为 tryCatch 调用的值返回。

下面是一个玩具示例:

F.errorAndWarning <- function() {
    warning("Warning before the error")
    cat("I have moved on.")
    stop("error")
    TRUE
}
F.error <- function() {stop("error"); TRUE}

test <- function(F)
  tryCatch(expr= {F}()
          , error=function(e)   cat("ERROR CAUGHT")
          , warning=function(w) cat("WARNING CAUGHT")
          )
test(F.error)
# ERROR CAUGHT
test(F.errorAndWarning)
# WARNING CAUGHT

预期/理想输出:

test(F.errorAndWarning)
# ERROR CAUGHT
这与

"是否可以处理警告,然后让函数继续,同时仍然捕获错误?"问题有关。

我有一个类似的问题,我想将警告和错误的日志记录函数绑定到 try catch,并在发出警告后始终继续,并且还能够对 try catch 执行多次尝试,例如访问脆弱的网络驱动器。这就是我最终使用的。这个或更简化的版本可以帮助您了解您的追求。

MyLibrary.Sys.Try <- function(
    expr,                   # Expression that will be evaluated by the call to Try
    errorMessage="",        # Optional prepended string to add to error output
    warningMessage="",      # Optional prepended string to add to warning output
    failureMessage="",      # Optional prepended string to add to failing all attempts
    finally=NULL,           # Optional expression to be executed at the end of tryCatch
    attempts=1,             # Number of attempts to try evaluating the expression
    suppressError=TRUE,     # Should the call just log the error or raise it if all attempts fail
    quiet=FALSE             # Return expression normally or as invisible
) {
    tryNumber <- 0
    while(tryNumber<attempts) {
        tryNumber <- tryNumber + 1
        # If not suppressing the error and this is the last
        # attempt then just evaluate the expression straight out
        if(tryNumber == attempts && !suppressError){
            # NOTE: I think some warnings might be lost here when
            # running in non-interactive mode. But I think it should be okay
            # because even nested dispatch seems to pick them up:
            # MyLibrary.Sys.Try(MyLibrary.Sys.Try(function(),suppressError=F))
            return(expr)
        }
        tryCatch({
            # Set the warning handler to an empty function
            # so it won't be raised by tryCatch but will
            # be executed by withCallingHandlers
            options(warning.expression=quote(function(){}))
            withCallingHandlers({
                if(quiet) {
                    return(invisible(expr))
                } else {
                    return(expr)
                }
            },error=function(ex){
                MyLibrary.Sys.Log.Error(errorMessage,ex)
            }, warning=function(warn){
                # Had issues with identical warning messages being
                # issued here so to avoid that only log it the first
                # time it happens in a given minute. 
                warnStamp <- paste(Sys.time(),warn,collapse="_",sep="")
                if(!identical(warnStamp,getOption("MyLibrary.LAST.WARNING"))) {
                    if(!(interactive() && is.null(getOption("warning.expression")))){
                        MyLibrary.Sys.Log.Warning(warningMessage,warn)
                    }
                    options(MyLibrary.LAST.WARNING=warnStamp)
                }
            })
        },error=function(ex){
            # Suppressing the error since it's been logged
            # by the handler above. Needs to be suppressed
            # to not invoke the stop directly since the
            # handler above passes it through.
        },finally={
            # Set the warning handler to the default value
            # of NULL which will cause it to revert to it's
            # normal behaviour. If a custom handler is normally
            # attached it would make sense to store it above
            # and then restore it here. But don't need that now
            options(warning.expression=NULL)
            if(!is.null(finally)){
                if(quiet) {
                    return(invisible(finally))
                } else {
                    return(finally)
                }
            }
        })
    }
    msg <- paste(ifelse(nchar(failureMessage)>0," - ",""),"Failed to call expression after ",attempts," attempt(s)",sep="")
    MyLibrary.Sys.Log.Error(failureMessage,msg)
}

我会编写一个执行expression并确定错误优先级的函数。

prioritize.errors <- local({
    # this function executes an expression and stores the warnings 
    # until it finishes executing.
    warnings <- list()
    w.handler <- function(w) {
        warnings <<- c(warnings, list(w))
        invokeRestart('muffleWarning') # here's the trick
    }
    function(expr) {
        withCallingHandlers({expr}, warning=w.handler)
        for (w in warnings) warning(w)
        warnings <<- list()
    }
})
F.warning <- function() {
    warning("a warning")
    message('ok')
}

test <- function(expr) {
    tryCatch(expr, 
        error=function(e) cat("ERROR CAUGHT"), 
        warning=function(w) cat("WARNING CAUGHT")
    )
}
test(prioritize.errors(F.error()))
# ERROR CAUGHT 
test(prioritize.errors(F.warning()))
# ok
# WARNING CAUGHT
test(prioritize.errors(F.errorAndWarning()))
# I have moved on.ERROR CAUGHT

我在 pander 包中编写了一个方便的帮助程序函数用于评估,它捕获所有警告、错误和标准输出上打印的任何内容,以及调用中返回的原始 R 对象:

> library(pander)
> evals('F.errorAndWarning()')
[[1]]
$src
[1] "F.errorAndWarning()"
$result
NULL
$output
NULL
$type
[1] "error"
$msg
$msg$messages
NULL
$msg$warnings
[1] "Warning before the error"
$msg$errors
[1] "error"

$stdout
[1] "I have moved on."
attr(,"class")
[1] "evals"

这三个答案都非常广泛和不错的工作,但我认为很多人也在寻找一种简短、简单的方法来处理警告,但要继续执行。正如Matthew所展示的那样,这可以很快完成:通过调用重新启动(并使用withCallingHandlers):

test <- function(FUN) withCallingHandlers(
  expr=FUN(),
  error=function(e) cat("ERROR CAUGHTn"),
  warning=function(w) {cat('WARNING CAUGHTn'); invokeRestart(findRestart('muffleWarning'))}
)

这仍会打印警告(即使稍后生成错误),但会继续执行

最新更新