Skip to content

[R-pkg-devel] conditional import of a package?

7 messages · Adelchi Azzalini, Brian G. Peterson, Duncan Murdoch +1 more

#
Hi. I am working on the development of an existing package (smof, on CRAN). My current aim is widen the list of possible optimizers from which the user can select one method for optimizing a certain task. Well-known possibilities within the base package are optim (with various options) and nlminb. Besides these, I am thinking of including also those of package nloptr, but without forcing users to install this package which perhaps they don't need for other purposes. Hence, I would like to import nloptr only if it is available on the user system; it not, I can just confine the list of optimizers to optim and nlminb.  

This idea implies a sort of ?conditional import? of nloptr. Is this possible? Section 1.1.3.1 "Suggested packages" of
https://translation.r-project.org/man/R-exts/R-exts-ko.html#Suggested-packages
seems to hint at such a possibility.  See the use of requireNamespace in the second paragraph.

After elaborating along this line, I packaged my code, with nloptr listed on the line Suggests of DESCRIPTION.  However this attempt failed a the ?R CMD check ? stage with message

Namespace dependency missing from DESCRIPTION Imports/Depends entries: ?nloptr?

In addition, I have no idea of how to declare a "conditional import? in NAMESPACE. 

Is this idea of ?conditional import? totally unfeasible, then?

---
Adelchi Azzalini
http://azzalini.stat.unipd.it
#
Adelchi,

The package 'PortfolioAnalytics', which we maintain, uses this pattern
extensively to support different optimization solvers. ?The packages
are all in 'Suggests' rather than 'Imports', because we don't want them
all required all the time.

So the code pattern is something like this:

if(optimize_method=="pso"){
? ?stopifnot("package:pso" %in% search() ||
requireNamespace("pso",quietly = TRUE) )
? # ...
}

where all the code that requires the 'pso' engine in this example is
contained inside the if block and the stopifnot check.

Regards,

Brian
#
On 2024-12-04 1:25 p.m., Adelchi Azzalini wrote:
The usual way to do this is to list the package in Suggests, and then 
wrap any use of it in `if (requireNamespace("pkg")) { ... }` blocks. 
This doesn't quite import the functions, you would need to use the 
`pkg::fn` syntax to access the functions.

If you really want to simulate importing so that you don't need the 
`pkg::` prefix, you could do it this way:  In the `.onLoad` function of 
your package, you would have code like

   if (requireNamespace("pkg")) {
     foo <- pkg::foo
     bar <- pkg::bar
   } else {
     foo <- stub
     bar <- stub
   }

where `stub` is a function that says "you need `pkg` to use this function".

Duncan Murdoch
#
Thanks, Duncan, for the kind advice. 

In my understanding, this is what I have done (or I tried to do).  My code is as follows:

  opt.methods <- c("Nelder-Mead", "BFGS", "nlminb")
  if(requireNamespace("nloptr", quietly = TRUE)) {
    nloptr.methods <- c("newuoa", "bobyqa", "cobyla")
    if(opt.method %in%  nloptr.methods) require(nloptr, quietly=TRUE)
    opt.methods <- c(opt.methods, nloptr.methods)
   }

<skip>

   if(opt.method %in% nloptr.methods) {    
      pos <- match("package:nloptr", search())
      nloptr.method <-  get(paste("nloptr", opt.method, sep="::"), pos=pos)
      opt <- nloptr.method(<args>)
      }
       
In the DESCRIPTION file there is
   Suggests: ggplot2, survival, nloptr

However, when I run ?R CMD check <etc>?, I get the following message 
* checking package dependencies ... ERROR
Namespace dependency missing from DESCRIPTION Imports/Depends entries: ?nloptr?

See section ?The DESCRIPTION file? in the ?Writing R Extensions?
manual.
* DONE

And - admittedly - I have no idea about how to insert appropriate import statements in NAMESPACE.


Best regards,

Adelchi
#
Le 05/12/2024 ? 09:06, Adelchi Azzalini a ?crit?:
It probably means that despite your intention, there is somewhere in
your code a place where you call nloptr::some_fun() out of the
'if(requireNamespace("nloptr", quietly = TRUE)) {...}' scope.

You can try to locally uninstall nloptr, then run 'R CMD check ...' to see where it happens and why.

Best,
Serguei.
#
On 2024-12-05 3:06 a.m., Adelchi Azzalini wrote:
You shouldn't use `require(nloptr, quietly=TRUE)`.  That puts it on the 
search list, and packages should generally not modify the search list.

Just before this in your code, you know that nloptr is loaded.  You 
don't know if it's on the search list or not, and that shouldn't matter.
The code above relies on having nloptr on the search list, and you don't 
know that, so you'll need a different way to set nloptr.method.  Another 
problem is that at this point you don't know if nloptr is loaded or not, 
because the requireNamespace() call above might have returned FALSE.

What I would do is to set nloptr.method in the code block above, i.e. 
change the first block to

     opt.methods <- c("Nelder-Mead", "BFGS", "nlminb")
     if(requireNamespace("nloptr", quietly = TRUE)) {
       nloptr.methods <- c("newuoa", "bobyqa", "cobyla")
       if(opt.method %in%  nloptr.methods)
         nloptr.method <- get(opt.method,
                              envir = loadNamespace("nloptr"))
     } else
       nloptr.method <- function(...) stop("this optimization needs the 
nloptr package.")


The second block could then be simplified to

      if(opt.method %in% nloptr.methods)
         opt <- nloptr.method(<args>)
That should be fine.
You shouldn't need to do that.

Duncan Murdoch
#
Hi. Thanks for the additional advice. It now works!

I only had to move the assignment of  nloptr.methods outside the if(.) block in the first chunk of code, otherwise the variable was undefined for the second block. This issue only showed up when I tried the newly created package on a machine without nloptr installed.

Best regards, and thanks again for your kind help.

Adelchi