When Writing My Own R Package, I Can't Seem to Get Other Packages to Import Correctly

import all the functions of a package except one when building a package

The NAMESPACE file is somewhat flexible here, as described in Writing R Extensions.

The two main import directives are:

import(PACKAGE)

which imports all objects in the namespace into your package. The second option is to do specific imports using:

importFrom(PACKAGE, foo)

which gives you access to foo() without needing the fully qualified reference PACKAGE::foo().

But these aren't the only two options. You can also use the except argument to exclude just a handful of imports:

import(PACKAGE, except=c(foo,bar))

which gives you everything from PACKAGE's namespace but foo() and bar(). This is useful - as in your case - for avoiding conflicts.

For roxygen, great catch on figuring out that you can do:

#' @rawNamespace import(PACKAGE, except = foo)

to pass a raw NAMESPACE directive through roxygen.

Updated R, copied library, still can't find packages, and can't seem to install all dependencies

The tidyverse package hardly contains anything: its main purpose is to get a bunch of other packages loaded. Removing it and updating it won't help update the other packages. You could do what @phago29 suggested in their comment, but an easier way is just to update everything. Run this command in an R session:

update.packages(ask = FALSE, checkBuilt = TRUE)

A few notes:

  • If you have admin capabilities, run as admin, and it will update your main library. If you don't, it'll install new copies in your user account.

  • Even though you're saying ask = FALSE, it may ask some questions about whether you can build packages from source. Answer "Yes" if your system is set up to do that, "No" if not. If you're not sure, try "Yes", and if you get install failures, run it again and say "No".)

  • This is likely to run for a while, so go away and have a coffee or something.

Conflicted package in R package building

If you are writing your own package and using external dependencies, you should not load them through repeated calls to library.

The proper way to do it is to state your dependencies in the DECRIPTION file of your package, which will mean that your dependencies are put on the search path in the correct order when your package is loaded. In your case, this removes the need for conflict_prefer, as dplyr will be higher up on the search path than stats. It also makes your package portable, because anyone who installs your package will have any missing dependencies installed automatically according to the packages listed in your DESCRIPTION file. Furthermore, doing it this way allows you to specify a minimum version of the dependency, so that anyone who already has an older version of the dependency installed will not come up against an obscure error when they try to use your package.

The DESCRIPTION file resides in the root directory of your package. It is a simple text file.

You need only add:

Depends:
tibble,
readxl,
dplyr,
stringr,
tidyr,
vars,
conflicted,
forecast,
lubridate

within this file, and your dependencies will be loaded with your package.

R import all but a couple of functions

Currently my best idea is

all <- getNamespaceExports("grid")
paste("@importFrom grid", paste(all[!(all %in% c("arrow", "unit"))], collapse = " "))
#[1] "@importFrom grid grid.edit pop.viewport ...

That's obviously not a good solution, but unlike for exports you can't use a regex for imports, i.e., there is no importPatternFrom.

How do I import %% when writing an R package?

Create a reexports.R file in your package with the following lines:

#' @importFrom magrittr %>%
#' @export
magrittr::`%>%`

This will make the pipe available to your package and also reexport it to users of your package, so when they load or attach your package the pipe will be available to them (they won’t have to also load magrittr). This can be automated with usethis::use_pipe() (see https://usethis.r-lib.org/reference/use_pipe.html). As @user2554330 mentions below, this solution depends on the use of roxygen2.

R Package: how import works when my exported function does not call explicitly a function from other packages, but a subroutine does

My understanding is that there are three "correct" ways to do the import. By "correct," I mean that they will pass CRAN checks and function properly. Which option you choose is a matter of balancing various advantages and is largely subjective.

I'll review these options below using the terminology

  • primary_function the function in your package that you wish to export
  • hidden the unexported function in your package used by primary_function
  • thirdpartypkg::blackbox, blackbox is an exported function from the thirdpartypkg package.

Option 1 (no direct import / explicit function call)

I think this is the most common approach. thirdpartypkg is declared in the DESCRIPTION file, but nothing is imported from thirdpartypkg in the NAMESPACE file. In this option, it is necessary to use the thirdpartypkg::blackbox construct to get the desired behavior.

# DESCRIPTION

Imports: thirdpartypkg

# NAMESPACE
export(primary_function)

#' @name primary_function
#' @export

primary_function <- function(x, y, z){
# do something here
hidden(a = y, b = x, z = c)
}

# Unexported function
#' @name hidden

hidden <- function(a, b, c){
# do something here

thirdpartypkg::blackbox(a, c)
}

Option 2 (direct import / no explicit function call)

In this option, you directly import the blackbox function. Having done so, it is no longer necessary to use thirdpartypkg::blackbox; you may simply call blackbox as if it were a part of your package. (Technically it is, you imported it to the namespace, so there's no need to reach to another namespace to get it)

# DESCRIPTION

Imports: thirdpartypkg

# NAMESPACE
export(primary_function)
importFrom(thirdpartypkg, blackbox)

#' @name primary_function
#' @export

primary_function <- function(x, y, z){
# do something here
hidden(a = y, b = x, z = c)
}

# Unexported function
#' @name hidden
#' @importFrom thirdpartypkg blackbox

hidden <- function(a, b, c){
# do something here

# I CAN USE blackbox HERE AS IF IT WERE PART OF MY PACKAGE
blackbox(a, c)
}

Option 3 (direct import / explicit function call)

Your last option combines the the previous two options and imports blackbox into your namespace, but then uses the thirdpartypkg::blackbox construct to utilize it. This is "correct" in the sense that it works. But it can be argued to be wasteful and redundant.

The reason I say it is wasteful and redundant is that, having imported blackbox to your namespace, you're never using it. Instead, you're using the blackbox in the thirdpartypkg namespace. Essentially, blackbox now exists in two namespaces, but only one of them is ever being used. Which begs the question of why make the copy at all.

# DESCRIPTION

Imports: thirdpartypkg

# NAMESPACE
export(primary_function)
importFrom(thirdpartypkg, blackbox)

#' @name primary_function
#' @export

primary_function <- function(x, y, z){
# do something here
hidden(a = y, b = x, z = c)
}

# Unexported function
#' @name hidden
#' @importFrom thirdpartypkg blackbox

hidden <- function(a, b, c){
# do something here

# I CAN USE blackbox HERE AS IF IT WERE PART OF MY PACKAGE
# EVEN THOUGH I DIDN'T. CONSEQUENTLY, THE blackbox I IMPORTED
# ISN'T BEING USED.
thirdpartypkg::blackbox(a, c)
}

Considerations

So which is the best approach to use? There isn't really an easy answer to that. I will say that Option 3 is probably not the approach to take. I can tell you that Wickham advises against Option 3 (I had been developing under that framework and he advised me against it).

If we make the choice between Option 1 and Option 2, the considerations we have to make are 1) efficiency of writing code, 2) efficiency of reading code, and 3) efficiency of executing code.

When it comes to the efficiency of writing code, it's generally easier to @importFrom thirdpartypkg blackbox and avoid having to use the :: operator. It just saves a few key strokes. This adversely affects readability of code, however, because now it isn't immediately apparent where blackbox comes from.

When it comes to efficiency of reading code, it's superior to omit @importFrom and use thirdpartypkg::blackbox. This makes it obvious where blackbox comes from.

When it comes to efficiency of executing code, it's better to @importFrom. Calling thirdpartypkg::blackbox is about 0.1 milliseconds slower than using @importFrom and calling blackbox. That isn't a lot of time, so probably isn't much of a consideration. But if your package uses hundreds of :: constructs and then gets thrown into looping or resampling processes, those milliseconds can start to add up.

Ultimately, I think the best guidance I've read (and I don't know where) is that if you are going to call blackbox more than a handful of times, it's worth using @importFrom. If you will only call it three or four times in a package, go ahead and use the :: construct.



Related Topics



Leave a reply



Submit