【问题标题】:Handling errors before warnings in tryCatch在 tryCatch 中的警告之前处理错误
【发布时间】:2013-10-26 08:43:53
【问题描述】:

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

通常,警告会导致错误。在这些情况下,我想忽略警告并仅处理错误。

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

我正在尝试使用众所周知的易于使用的tryCatch

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

我对 ?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

【问题讨论】:

  • 你最终使用了这里的任何答案吗?
  • 马特,我使用了你和汉斯答案的混合体。我不确定在哪里放置复选标记。我将把它放在 Hansi 上,因为我认为他高度评论的代码将有助于其他人搜索——另外他可以使用一些额外的点 :) 谢谢你,并提醒

标签: r exception-handling try-catch


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    这涉及到“是否可以处理警告,然后在继续捕获错误的同时让函数继续执行?” 问题。

    我有一个类似的问题,我想将警告和错误的日志记录功能绑定到 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)
    }
    

    【讨论】:

      【解决方案3】:

      我会编写一个函数来执行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
      

      【讨论】:

      • 这看起来很有帮助,谢谢。我得消化一分钟
      【解决方案4】:

      我在 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"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-25
        • 1970-01-01
        • 2013-02-24
        • 2015-09-30
        • 1970-01-01
        • 1970-01-01
        • 2018-04-22
        • 1970-01-01
        相关资源
        最近更新 更多