I am writing an R package pkgA
that is extending another package, pkgB
. In particular, I implemented a new class gas_planet
that extends the functionality of a class planet
written in pkgB
. When I use devtools::load_all()
, I have no problems in extending a generic function pkgB::hello()
to hello.gas_planet()
. However, when running devtools::check()
, examples calling the generic function from pkgB
halt with the following error:
> checking examples ... ERROR
Running examples in 'pkgA-Ex.R' failed
The error most likely occurred in:
> base::assign(".ptime", proc.time(), pos = "CheckExEnv")
> ### Name: hello.gas_planet
> ### Title: Say hello to a gas planet
> ### Aliases: hello.gas_planet
>
> ### ** Examples
>
> planet <- set_gas_planet("Jupiter")
> hello(planet)
Error in hello(planet) : could not find function "hello"
Execution halted
The code from the mother package pkgB
is:
#' Set planet
#'
#' @param name planet name
#'
#' @return planet object
#' @export
set_planet <- function(name){
planet <- list(name = name)
class(planet) <- "planet"
return(planet)
}
#' Say hello to a planet
#'
#' @param planet a planet object
#' @param ... More arguments
#'
#' @export
hello <- function(planet, ...) {
UseMethod("hello", planet)
}
with NAMESPACE
# Generated by roxygen2: do not edit by hand
export(hello)
export(set_planet)
My child package pkgA
contains the code:
#' Set gas planet
#'
#' @inheritParams pkgB::set_planet
#'
#' @return planet object
#' @export
set_gas_planet <- function(name){
planet <- list(name = name, structure = "rocky")
class(planet) <- c("gas_planet", "planet")
return(planet)
}
#' Say hello to a gas planet
#'
#' @inheritParams pkgB::hello
#'
#' @import pkgB
#'
#' @examples
#' planet <- set_gas_planet("Jupiter")
#' hello(planet)
#'
#' @export
hello.gas_planet <- function(planet, ...) {
print(paste0("Hello, ", planet$name, "! You look breezy today."))
}
with NAMESPACE file
# Generated by roxygen2: do not edit by hand
S3method(hello,gas_planet)
export(set_gas_planet)
import(pkgB)
From this post, I learned that @importFrom
is not needed. In any case, switching from @import pkgB
to @importFrom pkgB
did not solve the problem.
The solution I came up with after reading above mentionned post is adding @export hello
below @export
in the specification of hello.gas_planet()
. This makes the error message disappear but instead, I get the warning:
> checking for missing documentation entries ... WARNING
Undocumented code objects:
'hello'
All user-level objects in a package should have documentation entries.
See chapter 'Writing R documentation files' in the 'Writing R
Extensions' manual.
Writing a documentation for a generic function from another package appears to me as an unnecessary and possibly dangerous redundancy. What is the correct way of inheriting S3 generics from another package without getting any problems in devtools::check()
?
EDIT:
For reproducing the error, I created two GitHub repositories for pkgA
and pkgB
. I produced the error on Windows 10 and Windows 11 using R 4.3.1 and 4.3.3, respectively. I installed both packages locally. The error occurs when using devtools::check()
or after installing the package using devtools::install()
. Running the examples after using devtools::load_all()
works fine.
The issue is that you are calling the unqualified hello()
function in your example. But the example is executed in the global namespace, and you didn’t attach ‘pkgB’, nor did you reexport pkgB::hello()
from ‘pkgA’. So it’s not visible in the global namespace. That’s why your example fails.
To fix this, either (import and) reexport pkgB::hello()
from ‘pkgA’, or explicitly qualify hello()
in the example as pkgB::hello()
.
To reexport a name (such as an S3 generic) from another package, use
#' @importFrom pkgB hello
#' @export
pkgB::hello
This way you also don’t need a general @import pkgB
directive.
(There’s unfortunately a bug in the current version (7.3.1) of ‘roxygen2’ which generates an incorrect NAMESPACE
directive for S3 methods of imported generics (namely, export()
, whereas we want S3method()
). To work around this, either use @exportS3Method pkgB::hello
, or run devtools::document()
multiple times until the NAMESPACE
is no longer updated.)
Here’s the complete code for pkgA/R/hello.R
:
#' @importFrom pkgB hello
#' @export
pkgB::hello
#' Set gas planet
#'
#' @inheritParams pkgB::set_planet
#'
#' @return planet object
#' @export
set_gas_planet <- function(name) {
planet <- list(name = name, structure = "rocky")
class(planet) <- c("gas_planet", "planet")
planet
}
#' Say hello to a gas planet
#'
#' @inheritParams pkgB::hello
#'
#' @examples
#' planet <- set_gas_planet("Jupiter")
#' hello(planet)
#'
#' @export
hello.gas_planet <- function(planet, ...) {
print(paste0("Hello, ", planet$name, "! You look breezy today."))
}
Alternatively, here’s the solution without reexporting pkgB::hello()
:
#' Set gas planet
#'
#' @inheritParams pkgB::set_planet
#'
#' @return planet object
#' @export
set_gas_planet <- function(name) {
planet <- list(name = name, structure = "rocky")
class(planet) <- c("gas_planet", "planet")
planet
}
#' Say hello to a gas planet
#'
#' @inheritParams pkgB::hello
#'
#' @examples
#' planet <- set_gas_planet("Jupiter")
#' pkgB::hello(planet)
#'
#' @importFrom pkgB hello
#' @export
hello.gas_planet <- function(planet, ...) {
print(paste0("Hello, ", planet$name, "! You look breezy today."))
}