R package - Transferring environment from imported package - r

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?

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 = "redacted#redacted.com",
role = c("aut", "cre"))
Maintainer: Wouter van der Bijl <redacted#redacted.com>
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.

Related

R Package Check(): "All declared Imports should be used"

Starting Situation:
I'm writing a small package of functions for myself only (not CRAN; on GitHub, but not public), and developing locally on the computer. Mostly this is me being a newbie at R and learning to write first package.
I'm using devtools and after load_all() and check(), I have been getting this "NOTE":
-- R CMD check results -------------------------------------------------------------------------------------------- MondelezR 0.1.0 ----
Duration: 21.1s
> checking dependencies in R code ... NOTE
Namespace in Imports field not imported from: 'tibble'
All declared Imports should be used.
0 errors v | 0 warnings v | 1 note x
Question:
Am I doing something wrong or is this a known/ expected problem that I can ignore?
Little more background:
I am using tibble()
In my package, "Find in Files" shows that I have used tibble in four files in different ways:
DESCRIPTION file:
[First Section of File Omitted]
Encoding: UTF-8
RoxygenNote: 7.2.0
Imports:
stringr,
dplyr,
purrr,
tibble,
magrittr
Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
(I have not seen the message for the other imported packages.)
FUNCTION: mdlz_otm_filter.R
[omitted]
#' #examples
#' df_otm_final <- tibble::tibble(
[omitted]
I am only using tibble in the example, not in the function itself, and the relevant portion is shown above.
DOCUMENTATION: mdlz_otm_filter.Rd
The roxygen2 documentation created from the above function shows the exact same example, but as documentation.
TEST THAT: test-mdlz_make_KEY1.R
test_that("POSTAL LANE2 works as expected", {
df_test <- tibble::tibble(ORIG_ZIP = c("18615", "12345", "a5J 1u8"),
DEST_ZIP = c("1234", "23456", "i9y2b4"),
FINAL_KEY = c("18615-01234","12345-23456","A5J1U8-I9Y2B4"))
expect_identical(mdlz_make_POSTAL_LANE(df_test$ORIG_ZIP,
df_test$DEST_ZIP),
df_test$FINAL_KEY)
})
Attempt to remove tibble from DESCRIPTION
I tried removing tibble from Imports: on the DESCRIPTION file, but as I expected would happen, I got this instead:
-- R CMD check results -------------------------------------------------------------------------------------------- MondelezR 0.1.0 ----
Duration: 26.6s
> checking for unstated dependencies in examples ... WARNING
'::' or ':::' import not declared from: 'tibble'
> checking for unstated dependencies in 'tests' ... WARNING
'::' or ':::' import not declared from: 'tibble'
0 errors v | 2 warnings x | 0 notes v
So... warnings are worse than notes I figure.
Research:
Google search to start with brought me to these posts:
RStudio Community Meta-Package This guy's problem is that he needs to use functions in every package he's trying to put in his meta-package. My issue is I'm already using tibble and getting the note regardless.
SO devtools R CMD check NOTE But this one doesn't seem to apply because I AM using tibble in my package, and this guy is trying to remove it.
Help?
I don't know how to clear the note, if I should worry about it at all, or why I'm getting it since I am using tibble as shown above. Trying to learn, so an expository answer is appreciated. Thank you in advance.
It seems devtools check function is looking for an importFrom tag with the package tibble in some of your function documentation roxygen Docs.
Adding #importFrom tibble tibble to the functions documentation which use the library tibble might remove the note.

What's the preferred means for defining an S3 method in an R package without introducing a dependency?

I have an R package (not currently on CRAN) which defines a couple of S3 methods of generic functions from other packages (specifically knitr::knit_print and huxtable::as_huxtable). However, they're not a key part of my package, so I'd prefer not to create a dependency on those packages when a user installs my package. Up until R 4.0.0, I exported the S3 methods without importing the generics. Using roxygen2, my #export directive was translated into an export() directive in NAMESPACE rather than S3method(). This worked fine in R versions < 4.0.0 because R looks in the global environment for a matching generic_function.class method first rather than relying on proper registration of the S3 method. However, as per this blog on developer.r-project.org, R no longer looks for non-registered S3 methods.
What is the best way round this? For now, I've added #importFrom directives to my roxygen2 blocks and have added both packages to the imports section of DESCRIPTION. However, as I understand things this will mean any user installing my package will then also have to install knitr and huxtable whether they want to or not.
Fortunately, for R >= 3.6.0, you don't even need the answer by caldwellst. From the blog entry you linked above:
Since R 3.6.0, S3method() directives in NAMESPACE can also be used to perform delayed S3 method registration. With S3method(PKG::GEN, CLS, FUN) function FUN will get registered as an S3 method for class CLS and generic GEN from package PKG only when the namespace of PKG is loaded. This can be employed to deal with situations where the method is not “immediately” needed, and having to pre-load the namespace of pkg (and all its strong dependencies) in order to perform immediate registration is considered too “costly”.
Additionally, this is also discussed in the docs for the other suggestion of vctrs::s3_register():
#' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating
#' class creation in a vignette, since method lookup no longer always involves
#' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect
#' by using "delayed method registration", i.e. placing the following in your
#' `NAMESPACE` file:
#'
#' ```
#' if (getRversion() >= "3.6.0") {
#' S3method(package::generic, class)
#' }
So, you would simply need to not use #importFrom and instead of #export, use #exportS3Method package::generic (See https://github.com/r-lib/roxygen2/issues/796 and https://github.com/r-lib/roxygen2/commit/843432ddc05bc2dabc9b5b22c1ae7de507a00508)
Illustration
So, to illustrate, we can make two very simple packages, foo and bar. The package foo just has a generic foo() function and default method:
library(devtools)
create_package("foo")
#' foo generic
#'
#' #param x An object
#' #param ... Arguments passed to or from other methods
#' #export
foo <- function(x, ...) {
UseMethod("foo", x)
}
#' foo default method
#'
#' #param x An object
#' #param ... Arguments passed to or from other methods
#' #export
foo.default <- function(x, ...) {
print("Called default method for foo.")
}
After document() and install()ing, we create bar:
create_package("bar")
which creates a bar method for foo():
#' bar method for foo
#'
#' #param x A bar object
#' #param ... Arguments passed to or from other methods
#'
#' #exportS3Method foo::foo
foo.bar <- function(x, ...) {
print("Called bar method for foo.")
}
Importantly, we must load the foo package before running document(), or #exportS3Method won't work. That is,
library(foo)
document()
But, if we do that, we get the following in the NAMESPACE for bar:
# Generated by roxygen2: do not edit by hand
S3method(foo::foo,bar)
We have to manually add foo to "Suggests" in DESCRIPTION.
Then if we uninstall foo, we can still install bar:
> remove.packages("foo")
Removing package from ‘/home/duckmayr/R/x86_64-pc-linux-gnu-library/4.0’
(as ‘lib’ is unspecified)
> install("bar")
✓ checking for file ‘/home/jb/bar/DESCRIPTION’ ...
─ preparing ‘bar’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘bar_0.0.0.9000.tar.gz’
Running /opt/R/4.0.0/lib/R/bin/R CMD INSTALL \
/tmp/Rtmp5Xgwqf/bar_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/jb/R/x86_64-pc-linux-gnu-library/4.0’
* installing *source* package ‘bar’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (bar)
The vctrs package provides a function called s3_register that dynamically registers methods for use in the .onLoad function. You can read more about its use here, for yourself you would want:
.onLoad <- function(...) {
if (requireNamespace("knitr", quietly = TRUE)) {
vctrs::s3_register("knitr::knit_print", "class_name")
}
if (requireNamespace("huxtable", quietly = TRUE)) {
vctrs::s3_register("huxtable::as_huxtable", "class_name")
}
}
The documentation is kind as well so you don't have to import vctrs:
To avoid taking a dependency on vctrs for this one function, please feel free to copy and paste the function source into your own package.

r - document() package fails Error in if (pkg$package == "devtools") { : argument is of length zero

UPDATE
I have completed the package and it is hosted online at https://github.com/iembry-USGS/ie2misc.
Since the error message in the original post was not helpful, I attempted to roxygenize the package to see if that would work or not. Below are the commands and the error message.
library(roxygen2)
roxygenize(".", roclets = "rd")
# First time using roxygen2. Upgrading automatically...
# Error in parse(n = -1, file = file, srcfile = NULL,
# keep.source = FALSE) :
# 1:1: unexpected input
# 1: �
^
I am assuming that the unexpected input is referring to a character, but I don't know which file has the character in question.
Any assistance would be helpful.
Thank you.
UPDATE End
I am working on creating a package that contains 3 functions. I've been successful at creating 3 other packages using 1 function, but not with this package.
I have included the contents of the DESCRIPTION file below. Below that content is the code and the error that I am receiving when attempting to document this package.
Thank you.
Package: ie2misc
Title: Irucka Embry's Miscellaneous functions created while he was a
CNTS USGS Contractor.
Version: 1.0.0
Authors#R: person("Irucka", "Embry", , "", c("aut", "cre"))
Depends: R (>= 3.0.0), tcltk, data.table (>= 1.9.4)
Imports: openxlsx, gWidgets2, gWidgets2tcltk, stringi, qdap
Suggests: Rcpp (>= 0.11.5)
Maintainer: Irucka Embry <iembry#usgs.gov>
Description: Irucka Embry's Miscellaneous functions (processing exp files,
psf files, etc.) created while he was a Cherokee Nation Technology Solutions
(CNTS) USGS Contractor.
URL: https://gitlab.com/iembry/ie2misc
BugReports: https://gitlab.com/iembry/ie2misc/issues
License: CC0
Collate:
'ie2misc.R'
'psfFileChangeBATCH.R'
'psfFileChange.R'
'expFileOutput.R'
LazyData: true
Encoding: UTF-8
Then I run:
setwd("ie2misc"); library(devtools); document();
Updating documentation
Loading
Error in if (pkg$package == "devtools") { : argument is of length zero
I still don't know what the problem is, but I created an empty package with library(devtools). Then, I copied most of the ie2misc files into the newly created package. I also rewrote the DESCRIPTION file in RStudio. (Usually I use the Kate text editor for working on all files for R packages.) Once those steps were completed, I was able to document, check, and build the package.

How to properly include dependencies in R-package?

I was trying now for several hours to build a package in R and getting a bit desperate about how slowly I progress. I managed quite fast to build a package with no dependencies, everything works fine. Due to recommendations in several posts, I'm using R Studio, devtools and Roxygen2 (being on Windows). With dependencies, I get problems when I CHECK (e.g. with devtools::check() ):
checking dependencies in R code ... NOTE Namespace in Imports field
not imported from: 'ggplot2' All declared Imports should be used.
See the information on DESCRIPTION files in the chapter 'Creating R
packages' of the 'Writing R Extensions' manual.
Furthermore, check() deletes the import(ggplot2) line in the NAMESPACE. If I do check(document=F), it gives an cryptic error about a digest package which is not loaded. I read 'Writing R Extensions' - 1.1.3 Package Dependencies and Hadley's Wiki concerning how to write packages, but couldn't solve my problem. DESCRIPTION and NAMESPACE files of other R packages from CRAN don't look different to mine (for my eyes)?
Question: What am I doing wrong? Sorry for such a basic question, but I am at a loss and most step-by-step tutorials I've seen so far stop before explaining dependencies.
So far, I have 3 files:
A DESCRIPTION:
Package: test
Type: Package
Title: Foo
Version: 1.0
Date: 2014-03-21
Author: Bar
Maintainer: Foo <bar#mail.com>
Description: Blubb
Imports:
ggplot2
License: GPL-3
A NAMESPACE:
export(is.equal.null)
import(ggplot2)
A R-File:
#' Extension of compare to include NULLs
#'
#' Works as an extension to usual compare
#' Compares two basic objects which in addition to usual compare can be NULL
#' Intuitive output: TRUE if both are equal or NULL resp., FALSE if both are unequal or only one is NULL
#'
#' #param obj1 Basic object like \code{numeric, char, boolean, NULL}
#' #param obj2 Basic object like \code{numeric, char, boolean, NULL}
#' #keywords compare
#' #export
#' #examples
#' is.equal.null(5,5) # TRUE
#' is.equal.null(5,NULL) # FALSE
#' is.equal.null(NULL,NULL) # TRUE
is.equal.null <- function(obj1, obj2) {
# Small helper function to generalize comparison to comparison of NULL
# returns TRUE if both are NULL, and FALSE if only one of the objects is NULL
bool <- obj1==obj2
#qplot(obj1)
if (length(bool)) return(bool)
if (is.null(obj1) & is.null(obj2)) return(TRUE)
return(FALSE)
}
You need to declare imports in two places:
The DESCRIPTION file. You should have a line similar to:
Imports: ggplot2, pkg1, pkg2
The NAMESPACE file. Here you declare the packages you need
import(ggplot2)
or to avoid namespace clashes
importFrom(ggplot2, geom_point)
You can get roxygen2 to maintain the NAMESPACE file using the #import and #importFrom tags.
In your example your DESCRIPTION file looks OK, but you haven't added the necessary functions to the NAMESPACE.
A standard workflow, as described by Hadley, is:
library(devtools)
# Add a dependency
use_package('tibble')
# (Re-)build NAMESPACE
document()
# Reload the package: CTRL-L or
load_all()

devtools roxygen package creation and rd documentation

I am new to roxygen and am struggling to see how to be able to use it to quickly create a new/custom package.
I.e. I would like to know the minimum requirements are to make a package called package1 using devtools, roxygen2/3 so that I can run the commands
require(package1)
fun1(20)
fun2(20)
to generate 2000 and 4000 random normals respectively
So lets take the simplest example.
If I have two functions fun1 and fun2
fun1 <- function(x){
rnorm(100*x)
}
and
fun2 <- function(y){
rnorm(200*y)
}
the params are numeric, the return values are numeric. I'm pretty sure this isn't an S3 method, lets call the titles fun1 and fun2....im not too sure what other info i would need to provide. I can put fun1 and fun2 in separate .R files and add abit of #' but am unsure to include all relevant requirements for roxygen and also am unsure what to include as relevant requiremetns and how to use it to create the rd documentation to go with a package are. I presume the namespace would just have the names fun1 and fun2? and the package description would just be some generic information relating to me...and the function of the package?
any step by step guides would be gladly received.
EDIT: The below is how far I got to start with...
I can get as far as the following to create a pacakge...but cant use roxygen to make the documentation...
package.skeleton(list = c("fun1","fun2"), name = "package1")
and here is where I am not sure if I am missing a bunch of steps or not...
roxygenise("package1")
so when trying to install i get the following error message
system("R CMD INSTALL package1")
* installing to library ‘/Library/Frameworks/R.framework/Versions/2.15/Resources/library’
* installing *source* package ‘package1’ ...
** R
** preparing package for lazy loading
** help
Warning: /path.to.package/package1/man/package1-package.Rd:32: All text must be in a section
*** installing help indices
Error in Rd_info(db[[i]]) :
missing/empty \title field in '/path.to.package/package1/man/fun1.Rd'
Rd files must have a non-empty \title.
See chapter 'Writing R documentation' in manual 'Writing R Extensions'.
* removing ‘/Library/Frameworks/R.framework/Versions/2.15/Resources/library/package1’
I'm surprised #hadley says to not use package.skeleton in his comment. I would use package.skeleton, add roxygen comment blocks, then delete all the files in the "man" directory and run roxygenize. However, since Hadley says "Noooooooooo", here's the minimum you need to be able to build a package that passes R CMD check and exports your functions.
Create directory called "package1". Under that directory, create a file called DESCRIPTION and put this in it (edit it appropriately if you like):
DESCRIPTION
Package: package1
Type: Package
Title: What the package does (short line)
Version: 0.0.1
Date: 2012-11-12
Author: Who wrote it
Maintainer: Who to complain to <yourfault#somewhere.net>
Description: More about what it does (maybe more than one line)
License: GPL
Now create a directory called "R" and add a file for each function (or, you can put both of your functions in the same file if you want). I created 2 files: fun1.R and fun2.R
fun1.R
#' fun1
#' #param x numeric
#' #export
fun1 <- function(x){
rnorm(100*x)
}
fun2.R
#' fun2
#' #param y numeric
#' #export
fun2 <- function(y){
rnorm(200*y)
}
Now you can roxygenize your package
R> library(roxygen2)
Loading required package: digest
R> list.files()
[1] "package1"
R> roxygenize("package1")
Updating collate directive in /home/garrett/tmp/package1/DESCRIPTION
Updating namespace directives
Writing fun1.Rd
Writing fun2.Rd
Since you mentioned devtools in the title of your Q, you could use the build and install functions from devtools
build('package1')
install('package1')
Or you can exit R and use the tools that come with R to build/check/install.
$ R CMD build package1
$ R CMD check package1_0.0.1.tar.gz
$ R CMD INSTALL package1_0.0.1.tar.gz
Now, fire up R again to use your new package.
$ R --vanilla -q
library(package1)
fun1(20)
fun2(20)
But, figuring out the minimum requirements is unlikely to help you (or the users of your package) much. You'd be much better off studying one of the many, many packages that use roxgen2.
Here's a better version of the fun1.R file which still doesn't use all the roxygen tags that it could, but is much better than the bare minimum
Modified fun1.R
#' fun1
#'
#' This is the Description section
#'
#' This is the Details section
#'
#' #param x numeric. this is multiplied by 100 to determine the length of the returned vector
#' #return a numeric vector of random deviates of length \code{100 * x}
#' #author your name
#' #seealso \code{\link{fun2}}
#' #examples
#' fun1(2)
#' length(fun1(20))
#' #export
fun1 <- function(x){
rnorm(100*x)
}
Much later - You could let RoxygenReady prepare your functions with the minimal Roxygen annotation skeleton. It basically brings you from your 2 input functions to GSee's answer, which is the input of Roxygen2.

Resources