6

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:

R Yoda
  • 8,358
  • 2
  • 50
  • 87
  • 2
    I think there are changes coming in R-devel that will sub-class errors and such *a little*, but I don't know that they go to the fidelity that you're needing here. I think you're looking for something similar to "exit status" in many shell programs (`curl` has over 90), but I don't think that's readily available, and will certainly be per-`function`. I fear the only locale-aware version will be with `grep` or `%in%` :-( – r2evans Oct 22 '18 at 18:11
  • 1
    @r2evans Yes I heard rumors about improving the condition system too at ERUM 2018. `grep` is my best friend currently ;-) The package `rlang` is providing custom conditions (with sub classing) but this is only possible if the developers are aware of the problem and change the conditions they throw... – R Yoda Oct 22 '18 at 18:27
  • 2
    I'm not experienced in coding for locale and internationalization ... but I see the `translations` package, the `gettext` (and related) function, and [Translations30](https://developer.r-project.org/Translations30.html). Instead of `grep`, you might be able to use `gettext` to match the error/warning message, assuming that the author of the function created it with internationalization in mind. – r2evans Oct 22 '18 at 18:47

1 Answers1

2

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() 
        }
      }
    }
  }
}
gonzalez.ivan90
  • 1,322
  • 1
  • 12
  • 22
  • Thanks for your elaborated code implementing download/retry behaviour in R. Please note that this was just an example in my question and I am asking for a broader solution that is e. g. **not** dependent on the language settings (like it is in your case - `detailedError` is a localized message text). What I need is an even more generic solution to *"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?"* – R Yoda Nov 02 '18 at 18:16
  • In the absence of upper- or lower-class levels of error objects, I'll start knowing all posible errors R have. I've search for 'all R errors' with no good results. As you mentionated, grep could allow you categorize the kind of errors ... Is what I can imagne for now – gonzalez.ivan90 Nov 02 '18 at 21:00