R Yoda
R Yoda

Reputation: 8750

Reliably recognize and handle specific conditions like errors or warnings

I want to improve the R error handling by providing a function to handle specific (selected) conditions combined with a retry feature.

For example looped downloads should retry after timeouts or connection errors but stop immediately in case of other errors.

I cannot find a reliable way to identify a specific condition.

With "reliable" I mean something like a condition ID or at least different condition classes. My problem is:

  1. Error conditions thrown by base R (and also many packages using stop) seem not to use sub classes but (almost) always return simpleError, error and condition as class.

  2. Error messages may be localized (different languages) and could even change over time with new releases.

How can I reliably recognize a specific condition of base R or 3rd party packages independently of the R versions, platform (Win, OSX, Linux) and language setting?

I assume that I cannot modify the source code that throws the conditions (e. g. to add a sub class).

examine_condition <- function(exp) {
  cnd <- tryCatch(exp, error = function(e) e)
  str(cnd)  # show the internals
  invisible(cnd)
}

examine_condition(base::log("a"))
# List of 2
# $ message: chr "non-numeric argument to mathematical function"
# $ call   : language log("a")
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

examine_condition(base::colSums("a"))
# List of 2
# $ message: chr "'x' must be an array of at least two dimensions"
# $ call   : language base::colSums("a")
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

examine_condition(utils::read.csv(file = "this file does not exist.csv"))                  
# List of 2
# $ message: chr "cannot open the connection"
# $ call   : language file(file, "rt")
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

examine_condition(stop("my error"))
# List of 2
# $ message: chr "my error"
# $ call   : language doTryCatch(return(expr), name, parentenv, handler)
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

library(data.table)
data <- as.data.frame(mtcars)
examine_condition(data[, new_col := 99])  #  ":=" is data.table syntax!
# List of 2
# $ message: chr "Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in pa"| __truncated__
# $ call   : language `:=`(new_col, 99)
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

See also:

Upvotes: 6

Views: 514

Answers (1)

gonzalez.ivan90
gonzalez.ivan90

Reputation: 1360

You can get the class of cnd and check the kind of error. Here a little example about download files and how to deal with diferent errors:

# Get your items to download
downlodables <- c(paste0('https://www.google.com/', paste0(c('search?q=', '' ), month.name)))

# Iterate over them
for( i in 1:length(downlodables)){

  #Set a 
  dwnl <- tryCatch(download.file(url = downlodables[i], 
                                 destfile = paste0(getwd(),'/', i, '.htm'), 
                                 mode = 'wb'),
                   error = function (e) {e})


  # Check kind of error. Even numbers of 'i'
  class(dwnl) 

  # Check if some error appears
  if (any(class(dwnl) == 'error')){
    # or: any(class(dwnl) %in% c('error', 'warning'))

    # Print your error
    cat(paste0('\n Error found \n', dwnl, '\n'))
    write.csv(cbind(x = i, error = paste0(as.character(dwnl))), file = paste0(i, '.csv'), row.names = FALSE)

    # Conver to string your error
    detailedError <- as.character(dwnl$message) # not necessary

    # Make som in case of denied permisson
    if (any(grep('Permis', detailedError))){
      warning('U shall no pass!')
    }

    # Make som in case of time out conections. In this examlpe try 3 more times the download
    if (any(grep('time', detailedError))){
      count <- 0
      while(count =< 3){

        dwnl <- tryCatch(download.file(url = downlodables[i], 
                                       destfile = paste0(getwd(),'/', i, '.htm'), 
                                       mode = 'wb'),
                         error = function (e) {e})
        if(any(class(dwnl) == 'error')){
          count <- count + 1
        } else { 
          break() 
        }
      }
    }
  }
}

Upvotes: 2

Related Questions