Anders Ellern Bilgrau
Anders Ellern Bilgrau

Reputation: 10223

Proper use of optional package features and dependencies

I'm trying to provide some optional, convenient features to a package foo but I struggle with the dependencies of the dependencies. Specifically, I'd like to use some features from the bioconductor KEGGgraph-package if it is installed. KEGGgraph itself depends on the graph-package. From what I read (although it is not clear) is to include KEGGgraph (and graph?) as suggested.

From the documentation I see that requireNamespace is now encouraged together with usage of :: instead of require inside functions. Indeed the package check will also complain if require is used. However, when I use requireNamespace and :: the dependencies of KEGGgraph is not found.

Consider the following minimal reproducible example. With KEGGgraph installed and a fresh R-session. The function below cannot find a function in the graph-package:

# If KEGGgraph is not installed:
#source("http://bioconductor.org/biocLite.R")
#biocLite("KEGGgraph")

convenientFunction <- function() {
  if (requireNamespace("KEGGgraph")) {
    tmp.file <- paste0(tempfile(), ".kgml")
    kgml <- KEGGgraph::retrieveKGML("hsa:04115", organism = "hsa",
                                    destfile = tmp.file, method = "internal")
    return(KEGGgraph::parseKGML2DataFrame(tmp.file))
  } else {
    stop("This function needs KEGGgraph and its dependencies")
  }
}

convenientFunction()
#Loading required namespace: KEGGgraph
#trying URL 'http://www.genome.jp/kegg-bin/download?entry=hsa04115&format=kgml'
#Content type 'text/xml' length unknown
#opened URL
#.......... .......... ......
#downloaded 26 KB
#
# Error in nodeDataDefaults(gR, "KEGGNode") <- env.node : 
#  could not find function "nodeDataDefaults<-"

It is as-if requireNamespace does nothing. The error reproduce by running:

# In a new R-session...
requireNamespace("KEGGgraph") # With and without this line
tmp.file <- paste0(tempfile(), ".kgml")
kgml <- KEGGgraph::retrieveKGML("hsa:04115", organism = "hsa",
                                destfile = tmp.file, method = "internal")
KEGGgraph::parseKGML2DataFrame(tmp.file)

If require("KEGGgraph") has been called it works fine:

require("KEGGgraph")
res <- convenientFunction()

Is it really necessary for users to require this optional package manually? Adding && requireNamespace("graph") into the if-condition in the function definition does not seem to help.

Upvotes: 2

Views: 314

Answers (2)

Jitao David Zhang
Jitao David Zhang

Reputation: 33

I am the maintainer of KEGGgraph. I confirm the problem and has updated the package. In the Bioconductor subversion respository revision 103436, the issue is addressed and hopefully solved.

The solution is almost as Martin suggested, to explicitly import the function from the graph package. I took the chance also changed the dependency policy of KEGGgraph so that the graph package is imported from instead of being dependent on. Hopefully this makes KEGGgraph more friendly to uses cases illustrated above.

Thanks for reporting the issue, and thanks to Martin and Dirk for their answers.

Upvotes: 3

Martin Morgan
Martin Morgan

Reputation: 46866

This is a problem with KEGGgraph -- it uses but does not import graph::nodeDataDefaults<-. The solution is to get in touch with maintainer("KEGGgraph") and ask them to update their NAMESPACE

KEGGgraph$ svn diff
Index: NAMESPACE
===================================================================
--- NAMESPACE   (revision 104133)
+++ NAMESPACE   (working copy)
@@ -2,7 +2,7 @@

 importFrom(XML, "xmlAttrs", "xmlChildren", "xmlName", "xmlRoot", "xmlTreeParse")

-importFrom(graph, "edges", "nodes<-", "nodes", "edgeData")
+importFrom(graph, "edges", "nodes<-", "nodes", "edgeData", "nodeDataDefaults<-")

 exportPattern("^[^\\.]")
 exportMethods("getDisplayName",

It seems likely that there are other missing imports, the heavy-handed solution is to simply import(graph); a lighter though imperfect touch is to use codetoolsBioC::writeNamespaceImports() to generate the imports (this function is buggy so not to be relied on 100%).

A work-around is to say library(graph) or Depends: graph in your package; this will put graph on the search() path, and KEGGgraph will find the missing import there (this is why KEGGgraph itself works -- it Depends: on graph, so missing imports are found on the search path; when you requireNamespace(), you don't add KEGGgraph's Depends: packages to the search path, so nodeDataDefaults<- is not found).

Upvotes: 3

Related Questions