4

I have the following dependency chain in a package I'm developing:

  • My package uses a class (trajectory) defined in package A (simmer).
  • It also uses an S3 method for that class (plot.trajectory), which is defined in package B (simmer.plot).
  • I can import package A as a whole, but I cannot import package B as a whole, because it contains replacements for other functions defined in package A (the get_mon functions), so I get unwanted warnings about the original functions being replaced.

How do I use/import the S3 method without importing the rest of package B, preferably via roxygen2?

The roxygen2 documentation suggests the following:

If you want to add a new method to an S3 generic, import it with @importFrom pkg generic.

For my example, this would be @importFrom simmer.plot plot, but this returns a warning that plot is not exported by simmer.plot. The same thing happens if I import the generic first, using @importFrom graphics plot.

  • 2
    What you quoted there is if you're importing the *generic* from another package, and adding an S3 method in *your* package. Have you tried `#' @importFrom simmer.plot plot.trajectory`? – duckmayr Jan 08 '20 at 14:27
  • I have! I got a warning that `plot.trajectory` isn't exported by `simmer.plot`, even though I can see that it is on its GitHub repository: https://github.com/r-simmer/simmer.plot/blob/master/NAMESPACE – Accidental Statistician Jan 08 '20 at 23:15
  • 1
    Fair enough. That was just my best guess without having tried it. I was surprised you said it didn't work, so I did a quick search, and it seems like several others have ran into the same problem, all without **anyone** figuring it out! https://stackoverflow.com/q/23112762/8386140 https://stackoverflow.com/q/42952244/8386140 https://stackoverflow.com/q/32680169/8386140 If I don't have time to look into it and you don't get an answer, I might put up a bounty for this. Interesting question – duckmayr Jan 08 '20 at 23:30
  • Have you tried accessing the function with simmer.plot:::plot.trajectory()? – BigFinger Jan 09 '20 at 00:08
  • 1
    This doesn't solve this particular problem, because the overloaded functions are needed, as I stated in my answer below, but you only need to import a single valid function from a package and then the S3 methods registered there are readily available. See https://stackoverflow.com/a/59667634/6788081 – Iñaki Úcar Jan 09 '20 at 16:02

2 Answers2

2

Use (see this):

#' @rawNamespace import(simmer, except=c(get_mon_arrivals, get_mon_resources, get_mon_attributes))
#' @import simmer.plot

because you really need to use the overloaded functions in simmer.plot so that the plot methods there can work. An equivalent, but shorter version:

#' @rawNamespace import(simmer, except=getNamespaceExports("simmer.plot"))
#' @import simmer.plot
Iñaki Úcar
  • 935
  • 1
  • 5
  • 11
  • Thanks, wasn't expecting an answer from the `simmer` developer! Am I correct in thinking the overloaded versions are only needed for plot.attributes etc., not for plot.trajectory? – Accidental Statistician Jan 10 '20 at 15:06
  • Yeap, that's right. If you only intend to use `plot.trajectory`, then your solution is just fine. – Iñaki Úcar Jan 11 '20 at 18:27
2

Iñaki Úcar's mention of the @rawNamespace tag led me to work out a version that doesn't import any of package B's exported functions, using the getNamespaceExports function mentioned in this answer:

#' @rawNamespace import(packageB, except = getNamespaceExports("packageB"))

The @rawNamespace tag in roxygen2 inserts raw code into the NAMESPACE file. getNamespaceExports returns the names of all exported functions in a namespace: this can be a package that you haven't attached.

For my specific example, I can write this:

#' @import simmer
#' @rawNamespace import(simmer.plot, except = getNamespaceExports("simmer.plot"))

which puts these lines in the NAMESPACE:

import(simmer)
import(simmer.plot, except = getNamespaceExports("simmer.plot"))
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214