R package - Transferring environment from imported package
Asked Answered
K

1

1

Assume an R package (myPackage) that imports the R package RCircos via the DESCRIPTION file and the NAMESPACE file.

$ cat DESCRIPTION
Package: myPackage
Imports: RCircos (>= 1.2.0)
...

$ cat NAMESPACE
import(RCircos)
...

One of the perks of RCircos is that it defines a custom environment (called RCircos.Env) and reads/writes variables to this environment from various of its functions. For example, function RCircos.Initialize.Plot.Parameters reads and writes to this environment.

...
RCircosEnvironment <- NULL;
RCircosEnvironment <- get("RCircos.Env", envir = globalenv());
RCircosEnvironment[["RCircos.PlotPar"]] <- plot.param;

(This peculiar behavior has also been recognized by other R packages; see, for example, lines 247-249 of this package).

Unfortunately, it seems that the environment RCircos.Env is not recognized out of the box in myPackage when I simply import RCircos via the DESCRIPTION file and the NAMESPACE file.

So what can be done?

There seem to be two options of making the environment RCircos.Env accessible to functions like RCircos.Initialize.Plot.Parameters. However, both of these options cause the CRAN check (R CMD check myPackage --as-cran) to issue WARNINGs or NOTEs during the mandatory evaluation of myPackage prior to the submission to CRAN, thus preventing its acceptance on CRAN.

Option 1: I include the following line directly prior to the function demanding the object:

# my code here #
assign("RCircos.Env", RCircos::RCircos.Env, .GlobalEnv)
RCircos.Set.Core.Components(...)
# my code here #

However, the CRAN check highlights this line with a NOTE, thus preventing the acceptance of myPackage on CRAN.

* checking R code for possible problems ... NOTE
Found the following assignments to the global environment:
File ‘PACViR/R/visualizeWithRCircos.R’:
  assign("RCircos.Env", RCircos::RCircos.Env, .GlobalEnv)

Option 2: I load the entire RCircos library prior to the function demanding the object:

# my code here #
library(RCircos)
RCircos.Set.Core.Components(...)
# my code here #

However, the CRAN check highlights this option with a WARNING, again preventing the acceptance of myPackage on CRAN.

* checking dependencies in R code ... WARNING
'library' or 'require' call not declared from: ‘RCircos’
'library' or 'require' call to ‘RCircos’ in package code.
  Please use :: or requireNamespace() instead.
  See section 'Suggested packages' in the 'Writing R Extensions' manual.

Surely, there must be an easy and CRAN-compatible way of making the environment RCircos.Env accessible to functions such as RCircos.Set.Core.Components within myPackage! Can someone name and explain such a way?

Klute answered 3/7, 2019 at 18:21 Comment(8)
Have you tried exporting the environment? i.e. @importFrom RCircos RCircos.Env then @export using {roxygen2}Huonghupeh
How does one normally get the RCircos.Env in the global enviroment when using RCircos as a user? XGR seems to just call functions like RCircos.Set.Core.Components(), never assigning anything to the global environment as far as I can see.Richmond
@Richmond @Huonghupeh So, I did a little bit of digging. It seems that environments and their variables are not transferred when you import a package via NAMESPACE. That is why the environment RCircos.Env is not recognized in myPackage, even though I have RCircos as an import for myPackage in the DESCRIPTION file. Is that correct?Klute
You should be able to access RCircos::RCircos.Env, and you can re-export it, as seasmith said. That's e.g. what dplyr does with the %>% operator from magrittr. But that doesn't solve your CRAN issue that you aren't allowed to write to the global environmentRichmond
@Richmond Then how did RCircos get accepted to CRAN in the first place? (This seems to be a chicken/egg problem.)Klute
There is no code in RCircos like you have: assign("RCircos.Env", RCircos::RCircos.Env, .GlobalEnv). So I guess the question is why you need to have that assign.Richmond
@MichaelGruenstaeudl probably CRAN was less strict when RCircos was first submitted. Regardless, this is very bad behaviour; a package should never meddle with the global environment.Sixtynine
@Richmond A few minutes ago, I updated my question to better explain the underlying issue (see above). In essence, I need the assign or equivalent to have the environment RCircos.Env recognized in myPackage, because simply importing RCircos via the DESCRIPTION file and the NAMESPACE file apparently doesn't transfer the environment.Klute
R
1

Apparently the normal re-export does not work with environments as it does with functions. But this does work:

RCircos.Env <- RCircos::RCircos.Env

#' test
#'
#' @param ... data
#'
#' @export
test_fun <- function(...) {
  RCircos::RCircos.Set.Core.Components(...)
}

With DESCRIPTION:

Package: test
Type: Package
Title: test
Description: This is a description.
Version: 0.1.0
Authors@R: person("Wouter", "van der Bijl",
                  email = "[email protected]",
                  role = c("aut", "cre"))
Maintainer: Wouter van der Bijl <[email protected]>
License: GPL-3
Encoding: UTF-8
LazyData: true
Imports: RCircos
RoxygenNote: 6.1.1

And this NAMESPACE:

# Generated by roxygen2: do not edit by hand

export(test_fun)

Test with:

library(test)
data(UCSC.HG19.Human.CytoBandIdeogram, package = 'RCircos')
test_fun(UCSC.HG19.Human.CytoBandIdeogram)

Basically, when RCircos runs get("RCircos.Env", envir = globalenv()), it will traverse up the search path until it finds the RCircos.Env from your package instead.

When running R CMD Check I get 0 errors, 0 warnings, 0 notes.


Note that this strategy that RCircos uses, with an environment that gets looked up by using get(.., envir = globalenv()) is really unorthodox and generally not a good idea. R functions should generally not have side-effects, such as editing unseen environments. Setting default values etc. is typically done using options(). The whole package is probably not something you'd want to emulate, but at least now you can use it as a dependency.

Richmond answered 4/7, 2019 at 20:57 Comment(1)
Wow, I take my hat off to you! Your answer did indeed solve the issue. That being said, I still have to include export(RCircos.Env) in NAMESPACE (as you had suggested in an earlier version of your post) to make my package work.Klute

© 2022 - 2024 — McMap. All rights reserved.