How do you debug a generic function (using debug, or mtrace in the debug package)?
As an example, I want to debug cenreg in the NADA package, specifically the method that takes a formula input.
You can retrieve the method details like this:
library(NADA)
getMethod("cenreg", c("formula", "missing", "missing"))
function (obs, censored, groups, ...)
{
.local <- function (obs, censored, groups, dist, conf.int = 0.95,
...)
{
dist = ifelse(missing(dist), "lognormal", dist)
...
}
The problem is that cenreg itself looks like this:
body(cenreg)
# standardGeneric("cenreg")
I don't know how to step through the underlying method, rather than the generic wrapper.
To debug a function which is defined inside another function, single-step through to the end of its definition, and then call debug on its name. If you want to debug a function not starting at the very beginning, use trace(..., at = *) or setBreakpoint .
Generic functions (objects from or extending class genericFunction ) are extended function objects, containing information used in creating and dispatching methods for this function. They also identify the package associated with the function and its methods.
My first two suggestions are pretty basic: (1) wrap your function call in a try()
(that frequently provides more information with S4 classes) and (2) call traceback()
after the error is thrown (that can sometimes give hints to where the problem is really occuring).
Calling debug()
won't help in this scenario, so you need to use trace
or browser
. From the debug help page:
"In order to debug S4 methods (see Methods), you need to use trace, typically
calling browser, e.g., as "
trace("plot", browser, exit=browser, signature = c("track", "missing"))
S4 classes can be hard to work with; one example of this is the comment in the debug
package documentation (regarding the usage of mtrace()
with S4 classes):
"I have no plans to write S4 methods, and hope not to have to
debug other people’s!"
A similar question was asked recently on R-Help. The recommendation from Duncan Murdoch:
"You can insert a call to browser() if you want to modify the source. If
you'd rather not do that, you can use trace() to set a breakpoint in it.
The new setBreakpoint() function in R 2.10.0 will also work, if you
install the package from source with the R_KEEP_PKG_SOURCE=yes
environment variable set. It allows you to set a breakpoint at a
particular line number in the source code."
I've never done this before myself (and it requires R 2.10.0), but you might try installing from source with R_KEEP_PKG_SOURCE=yes
.
Incidentally, you can use the CRAN mirror of NADA in github to browse the source.
For a long time this was a standard annoyance point for S4 method debugging. As pointed out by Charles Plessy, I worked with Michael Lawrence to add a number of features to R that are intended to make this easier.
debug
, debugonce
, undebug
and isdebugged
all now take a signature argument suitable for specifying s4 methods. Furthermore, debugging S4 methods this way bypasses the weird implementation detail that you previously had to deal with by hand by browser
ing into the method via trace
, stepping through the .local
definition, debugging that, then continuing.
In addition, I added debugcall
, which you give an actual, full call that you would want to invoke. Doing so sets debugging on the first closue which will be invoked when evaluating that call that is not an S3 or S4 standard generic. So if you are calling a non-generic, that will just be the top level function being called, but if it is a standard S3 or S4 generic, the first method that will be hit is debugged instead of the generic. A "standard S3 generic" is defined as a function where the first top-level (ignoring curly braces) call in the body is a call to UseMethod.
Note we went back and forth on the design of this but at the end of the day settled on debugcall
not actually executing the function call being debugged, but it returns the call expression which you can pass it to eval
if desired, as illustrated in ?debugcall
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With