Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use S3 methods from another package which uses export rather than S3method in its namespace without using Depends or library()

I'm working on an R package at present and trying to follow the best practice guidelines provided by Hadley Wickham at http://r-pkgs.had.co.nz. As part of this, I'm aiming to have all of the package dependencies within the Imports section of the DESCRIPTION file rather than the Depends since I agree with the philosophy of not unnecessarily altering the global environment (something that many CRAN and Bioconductor packages don't seem to follow).

I want to use functions within the Bioconductor package rhdf5 within one of my package functions, in particular h5write(). The issue I've now run into is that it doesn't have its S3 methods declared as such in its NAMESPACE. They are declared using (e.g.)

export(h5write.default)
export(h5writeDataset.matrix)

rather than

S3method(h5write, default)
S3method(h5writeDataset, matrix)

The generic h5write is defined as:

h5write <- function(obj, file, name, ...) {
res <- UseMethod("h5write")
  invisible(res)
}

In practice, this means that calls to rhdf5::h5write fail because there is no appropriate h5write method registered.

As far as I can see, there are three solutions to this:

  1. Use Depends rather than Imports in the DESCRIPTION file.
  2. Use library("rhdf5") or require("rhdf5") in the code for the relevant function.
  3. Amend the NAMESPACE file for rhdf5 to use S3methods() rather than export().

All of these have disadvantages. Option 1 means that the package is loaded and attached to the global environment even if the relevant function in my package is never called. Option 2 means use of library in a package, which while again attaches the package to the global environment, and is also deprecated per Hadley Wickham's guidelines. Option 3 would mean relying on the other package author to update their package on Bioconductor and also means that the S3 methods are no longer exported which could in turn break other packages which rely on calling them explicitly.

Have I missed another alternative? I've looked elsewhere on StackOverflow and found the following somewhat relevant questions Importing S3 method from another package and How to export S3 method so it is available in namespace? but nothing that directly addresses my issue. Of note, the key difference from the first of these two is that the generic and the method are both in the same package, but the issue is the use of export rather than S3method.

Sample code to reproduce the error (without needing to create a package):

loadNamespace("rhdf5")
rdhf5::h5write(1:4, "test.h5", "test")

Error in UseMethod("h5write") : 
no applicable method for 'h5write' applied to an object of class
"c('integer', 'numeric')

Alternatively, there is a skeleton package at https://github.com/NikNakk/s3issuedemo which provides a single function demonstrateIssue() which reproduces the error message. It can be installed using devtools::install_github("NikNakk/s3issuedemo").

like image 555
Nick Kennedy Avatar asked Jun 11 '15 10:06

Nick Kennedy


1 Answers

The key here is to import the specific methods in addition to the generic you want to use. Here is how you can get it to work for the default method.

Note: this assumes that the test.h5 file already exists.

#' @importFrom rhdf5 h5write.default
#' @importFrom rhdf5 h5write
#' @export
myFun <- function(){
    h5write(1:4, "test.h5", "test")
}

I also have put up my own small package demonstrating this here.

like image 100
cdeterman Avatar answered Sep 22 '22 15:09

cdeterman