--- title: "Try Catch" author: "Colin Fay" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Try Catch} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( error = TRUE, collapse = TRUE, comment = "#>" ) library(attempt) ``` ## attempt `attempt()` is a wrapper around base `try()` that allows you to insert a custom messsage on error. ```{r} attempt(log("a")) # Error: non-numeric argument to mathematical function attempt(log("a"), msg = "Nop !") # Error: Nop ! ``` You can make it verbose (i.e. returning the expression): ```{r} attempt(log("a"), msg = "Nop !", verbose = TRUE) # Error in log("a"): Nop ! ``` Of course the result is returned if there is one: ```{r} attempt(log(1), msg = "Nop !", verbose = TRUE) # [1] 0 ``` As with `try()`, the result can be saved as an error object: ```{r} a <- attempt(log("a"), msg = "Nop !", verbose = TRUE) a # [1] "Error in log(\"a\"): Nop !\n" # attr(,"class") # [1] "try-error" # attr(,"condition") # ``` You can check if the result is an error with `is_try_error()` ```{r eval = TRUE} a <- attempt(log("a"), msg = "Nop !", verbose = FALSE) is_try_error(a) ``` ## silent_attempt `silent_attempt()` is a wrapper around `silently()` (see further down for more info) and `attempt()`. It attempts to run the expr, stays silent if the expression succeeds, and returns error or warnings if any. ```{r} silent_attempt(log("a")) # Error: non-numeric argument to mathematical function silent_attempt(log(1)) ``` ## try catch You can write a try catch with these params: + `expr` the expression to be evaluated + `.e` a mapper or a function evaluated when an error occurs + `.w` a mapper or a function evaluated when a warning occurs + `.f` a mapper or an expression which is always evaluated before returning or exiting In `.e` and `.f`, the `.x` refers to the error / warning object. ### With mappers ```{r} try_catch( expr = log("a"), .e = ~ paste0("There is an error: ", .x), .w = ~ paste0("This is a warning: ", .x) ) # [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n" try_catch( log("a"), .e = ~ stop(.x), .w = ~ warning(.x) ) # Error in log("a") : non-numeric argument to mathematical function try_catch( matrix(1:3, nrow = 2), .e = ~ print(.x), .w = ~ print(.x) ) # try_catch( expr = 2 + 2, .f = ~ print("Using R for addition... ok I'm out!") ) # [1] "Using R for addition... ok I'm out!" # [1] 4 ``` As usual, the handlers are set only if you call them: ```{r} try_catch(matrix(1:3, nrow = 2), .e = ~ print("error")) # [,1] [,2] # [1,] 1 3 # [2,] 2 1 # Warning message: # In matrix(1:3, nrow = 2) : # data length [3] is not a sub-multiple or multiple of the number of rows [2] ``` ```{r} try_catch(matrix(1:3, nrow = 2), .w = ~ print("warning")) # [1] "warning" ``` ### Traditionnal way `{attempt}` is flexible in how you can specify your arguments. You can, as you do with `{base}` `tryCatch()`, use a plain old function: ```{r} try_catch( log("a"), .e = function(e) { print(paste0("There is an error: ", e)) print("Ok, let's save this") time <- Sys.time() a <- paste("+ At", time, ", \nError:", e) # write(a, "log.txt", append = TRUE) # commented to prevent log.txt creation print(paste("log saved on log.txt at", time)) print("let's move on now") } ) # [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n" # [1] "Ok, let's save this" # [1] "log saved on log.txt at 2018-01-30 16:59:13" # [1] "let's move on now" ``` You can even mix both: ```{r} try_catch( log("a"), .e = function(e) { paste0("There is an error: ", e) }, .f = ~ print("I'm not sure you can do that pal !") ) # [1] "I'm not sure you can do that pal !" # [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n" try_catch( log("a"), .e = ~ paste0("There is an error: ", .x), .f = function() print("I'm not sure you can do that pal !") ) # [1] "I'm not sure you can do that pal !" # [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n" ``` ### `try_catch_df()` `try_catch_df()` returns a tibble with the call, the error message if any, the warning message if any, and the value of the evaluated expression or "error". The values will always be contained in a list-column. ```{r eval = TRUE} res_log <- try_catch_df(log("a")) res_log res_log$value res_matrix <- try_catch_df(matrix(1:3, nrow = 2)) res_matrix res_matrix$value res_success <- try_catch_df(log(1)) res_success res_success$value ``` ### `map_try_catch()` `map_try_catch()` and `map_try_catch_df()` allow you to map on a list of arguments `l`, to be evaluated by the function in `fun`. ```{r eval = TRUE} map_try_catch(l = list(1, 3, "a"), fun = log, .e = ~.x) map_try_catch_df(list(1, 3, "a"), log) ```