3

Suppose there is a set of functions, drawn from a package not written by me, that I want to assign to a special behavior on error. My current concern is with the _impl family of functions in dplyr. Take mutate_impl, for example. When I get an error from mutate, traceback almost always leads me to mutate_impl, but it is usually a ways up the call stack -- I have seen it be as many as 15 calls from the call to mutate. So what I want to know at that point is typically how the arguments to mutate_impl relate to the arguments I originally supplied to mutate (or think I did).

So, this code is probably wrong in too many ways to count -- certainly it does not work -- but I hope it at least helps to express my intent. The idea is that I could wrap it around mutate_impl, and if it produces an error, it saves the error message and a description of the arguments and returns them as a list

str_impl  <- function(f){tryCatch(f, error = function(c) {
    msg <- conditionMessage(c)
    args <- capture.output(str(as.list(match.call(call(f)))))
    list(message =  msg, arguments = args)
}
assign(str_impl(mutate_impl), .GlobalEnv)

Although, this still falls short of what I really want, because even without the constraint of working code, I could not figure out how to produce a draft. What I really want is to be able to identify a function or list of functions that I want to have this behavior on error, and then have it occur on error whenever and wherever that function is called. I could not think of any way to even start to do that without rewriting functions in the dplyr package environment, which struck me as a really bad idea.

The final assignment to the global environment is supposed to get the error object back to somewhere I can find it, even if the call to mutate_impl happens somewhere inaccessible, like in an environment that ceases to exist after the error.

andrewH
  • 2,281
  • 2
  • 22
  • 32

1 Answers1

0

Probably the best way of achieving what you want is via the trace functionality. It's surely worth reading the help about trace, but here is a working example:

library(dplyr)

trace("mutate_impl", exit = quote({
  if (class(returnValue())[1]=="NULL") {
    cat("df\n")
    print(head(df))
    cat("\n\ndots\n")
    print(dots)
  } else {
    # no problem, nothing to do
  }
}), where = mutate, print = FALSE)

# ok
xx <- mtcars %>% mutate(gear = gear * 2)
# not ok, extra output
xx <- mtcars %>% mutate(gear = hi * 2)

It should be fairly simple to adjust this to your specific needs, e.g. if you want to log to a file instead:

trace("mutate_impl", exit = quote({
  if (class(returnValue())[1]=="NULL") {
    sink("error.log")
    cat("df\n")
    print(head(df))
    cat("\n\ndots\n")
    print(dots)
    sink()
  } else {
    # no problem, nothing to do
  }
}), where = mutate, print = FALSE)
RolandASc
  • 3,863
  • 1
  • 11
  • 30