moodymudskipper / tags

A collection of tags built using the package tag
GNU General Public License v3.0
8 stars 0 forks source link

trying_and_catching ? #49

Open moodymudskipper opened 5 years ago

moodymudskipper commented 5 years ago

similar to suppressing_warnings but the argument given is a list of items where each items is either a regex pattern (in which case we "try"), or a formula in the format "foo" ~ {some_code}, in which case we tryCatch.

some_code can use ., which contains the error message.

conditions are used in turn until one matches, if none matches we trigger the original error.

moodymudskipper commented 5 years ago

this is related: https://codereview.stackexchange.com/questions/225419/error-specific-trycatch

moodymudskipper commented 5 years ago

Have also trying(), which has the same interface as suppressing_warnings()

moodymudskipper commented 5 years ago

These should be about right, maybe error messages could be better handled but good enough.

Just add small feature : suppressing_warnings and trying should support a vector of .regex, either try them in turn or just collapse the regex "(...)|(...).

trying <- tag::tag(
  args = alist(.regex="*", .silent = FALSE, 
               .outFile = getOption("try.outFile", default = stderr())),
  pattern = {
    # try call in parent env, keep "visible" info
    res <- try(
      withVisible(eval.parent(CALL(eval = FALSE))), 
      silent = TRUE, .outFile)

    # if error, see if it satisfies condition
    if(inherits(res, "try-error")) {
      cond <- if (is.character(.regex)) 
        grepl(.regex, res) else 
          rlang::as_function(.regex)(res)
      if(cond) message(res) else 
        stop(res, call. = FALSE)
      invisible(res)
    } else {
      # if no error, return result with relevant visibility
      if(res$visible) res$value else invisible(res$value)
    }
  })

x1 <- trying("xx")$stop("hello")
#> Error: Error in eval(expr, p) : hello
x1
#> Error in eval(expr, envir, enclos): object 'x1' not found
x2 <- trying("ll")$stop("hello")
#> Error in eval(expr, p) : hello
x2
#> [1] "Error in eval(expr, p) : hello\n"
#> attr(,"class")
#> [1] "try-error"
#> attr(,"condition")
#> <simpleError in eval(expr, p): hello>

trying_and_catching <- tag::tag(
  args = alist(.regex=, finally=),
  pattern = {
    # wrap .regex in list if it was given unwrapped
    if (inherits(.regex, "formula")) .regex <- list(.regex)
    on.exit(if(!missing(finally)) eval.parent(substitute(finally)))
  tryCatch(
    eval.parent(CALL(eval = FALSE)),
    error = function(e){
      # test all elements
      for (regex in .regex) {
        # it must be a two sided formula
        if(!rlang::is_formula(regex, lhs=TRUE))
          stop(".regex should be a 2 sided formula or a list of such elements")
        # regex[[2]] is lhs as call, regex[-3] is lhs as formula
        detected <- 
          if(is.character(regex[[2]])) {
            grepl(regex[[2]], e$message)
          } else {
            detect_fun <- rlang::as_function(regex[-3])
            detect_fun(e$message)
          }
        if(detected) {
          # regex[-2] is rhs as formula
          f <- rlang::as_function(regex[-2])
          return(f(e$message))
        }
      }
      stop(e)
    }
  )
})

# using regex
trying_and_catching("xx" ~ warning("foo",call. = F))$stop("hello")
#> Error in eval(expr, p): hello
trying_and_catching("ll" ~ warning("foo",call. = F))$stop("hello")
#> Warning: foo
trying_and_catching("ll" ~ warning("foo",call. = F), finally = print("baz"))$stop("hello")
#> [1] "baz"
#> Warning: foo
# using function
trying_and_catching(startsWith(.,"hell") ~ warning("bar",call. = F))$stop("hello")
#> Warning: bar

Created on 2019-08-29 by the reprex package (v0.3.0)

moodymudskipper commented 4 years ago

Maybe suppressing_warnings could also catch and rethrow warnings or errors ? A new name might be preferred then, though suppressing should be the default behavior.

OR we make trying_and_catching() handle warnings too.

OR we create 2 new tagsinstead : handling_errors() and handling_warnings(), but they don't sound so good...